runwork 0.25.2 → 0.26.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/bundled-types/client.d.ts +39 -0
- package/bundled-types/core-agent.d.ts +1 -1
- package/bundled-types/core-entity-do.d.ts +0 -2
- package/bundled-types/core-events.d.ts +0 -7
- package/bundled-types/core-file-storage.d.ts +0 -4
- package/bundled-types/core-utils.d.ts +104 -2
- package/bundled-types/core-workflows.d.ts +1 -1
- package/dist/index.js +1592 -223
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -61,7 +61,46 @@ import {
|
|
|
61
61
|
createInflate,
|
|
62
62
|
createInflateRaw
|
|
63
63
|
} from "node:zlib";
|
|
64
|
-
function
|
|
64
|
+
function firstEnv(names) {
|
|
65
|
+
const keys = Object.keys(process.env);
|
|
66
|
+
for (const candidate of names) {
|
|
67
|
+
const variable = keys.find((k) => k === candidate) ?? keys.find((k) => k.toLowerCase() === candidate.toLowerCase());
|
|
68
|
+
if (variable === undefined)
|
|
69
|
+
continue;
|
|
70
|
+
const value = process.env[variable];
|
|
71
|
+
if (value && value.trim() !== "")
|
|
72
|
+
return { variable, value: value.trim() };
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function isProxyExempt(hostname) {
|
|
77
|
+
const raw = process.env.NO_PROXY ?? process.env.no_proxy ?? "";
|
|
78
|
+
const host = hostname.toLowerCase();
|
|
79
|
+
for (const entry of raw.split(",")) {
|
|
80
|
+
const rule = entry.trim().toLowerCase().replace(/:\d+$/, "");
|
|
81
|
+
if (!rule)
|
|
82
|
+
continue;
|
|
83
|
+
if (rule === "*")
|
|
84
|
+
return true;
|
|
85
|
+
const bare = rule.startsWith(".") ? rule.slice(1) : rule;
|
|
86
|
+
if (host === bare || host.endsWith(`.${bare}`))
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
function proxyForUrl(url) {
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
parsed = new URL2(url);
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
if (isProxyExempt(parsed.hostname))
|
|
99
|
+
return null;
|
|
100
|
+
const schemeVars = parsed.protocol === "http:" ? ["HTTP_PROXY", "http_proxy"] : ["HTTPS_PROXY", "https_proxy"];
|
|
101
|
+
return firstEnv([...schemeVars, "ALL_PROXY", "all_proxy"]);
|
|
102
|
+
}
|
|
103
|
+
function pickTransport(url) {
|
|
65
104
|
if (transportOverride === "curl")
|
|
66
105
|
return "curl";
|
|
67
106
|
if (transportOverride === "node")
|
|
@@ -69,10 +108,14 @@ function pickTransport() {
|
|
|
69
108
|
const env = (process.env.RUNWORK_HTTP_TRANSPORT || "").toLowerCase().trim();
|
|
70
109
|
if (env === "curl")
|
|
71
110
|
return "curl";
|
|
111
|
+
if (env === "node")
|
|
112
|
+
return "node";
|
|
113
|
+
if (url && proxyForUrl(url))
|
|
114
|
+
return "curl";
|
|
72
115
|
return "node";
|
|
73
116
|
}
|
|
74
117
|
async function httpFetch(url, init = {}) {
|
|
75
|
-
const transport = pickTransport();
|
|
118
|
+
const transport = pickTransport(url);
|
|
76
119
|
if (transport === "curl")
|
|
77
120
|
return curlFetch(url, init);
|
|
78
121
|
return doRequest(url, init, 0);
|
|
@@ -789,6 +832,10 @@ class ApiClient {
|
|
|
789
832
|
async reportTelemetry(workspaceId, events) {
|
|
790
833
|
await this.request(`/api/workspaces/${workspaceId}/team/telemetry`, { method: "POST", body: JSON.stringify({ events }) });
|
|
791
834
|
}
|
|
835
|
+
async createDebugReport(workspaceId, body) {
|
|
836
|
+
const res = await this.request(`/api/workspaces/${workspaceId}/debug-reports`, { method: "POST", body: JSON.stringify(body) });
|
|
837
|
+
return res.data;
|
|
838
|
+
}
|
|
792
839
|
async createSharedConversation(workspaceId, body) {
|
|
793
840
|
const res = await this.request(`/api/workspaces/${workspaceId}/shared-conversations`, {
|
|
794
841
|
method: "POST",
|
|
@@ -999,10 +1046,13 @@ var init_workspace_state = __esm(() => {
|
|
|
999
1046
|
// src/utils/subprocess.ts
|
|
1000
1047
|
import {
|
|
1001
1048
|
execFileSync as cpExecFileSync,
|
|
1002
|
-
spawn as cpSpawn
|
|
1049
|
+
spawn as cpSpawn,
|
|
1050
|
+
spawnSync as cpSpawnSync
|
|
1003
1051
|
} from "child_process";
|
|
1004
1052
|
var execFileSync = (file, args, options) => {
|
|
1005
1053
|
return cpExecFileSync(file, args, { windowsHide: true, ...options ?? {} });
|
|
1054
|
+
}, spawnSync = (file, args, options) => {
|
|
1055
|
+
return cpSpawnSync(file, args, { windowsHide: true, ...options ?? {} });
|
|
1006
1056
|
}, spawn2 = (command, args, options) => {
|
|
1007
1057
|
return cpSpawn(command, args ?? [], { windowsHide: true, ...options ?? {} });
|
|
1008
1058
|
};
|
|
@@ -3096,13 +3146,56 @@ function bufToStr2(val) {
|
|
|
3096
3146
|
return val.toString("utf-8").trim();
|
|
3097
3147
|
return "";
|
|
3098
3148
|
}
|
|
3149
|
+
function pushSyncStash(cwd) {
|
|
3150
|
+
execFileSync("git", ["stash", "push", "-m", SYNC_STASH_TAG], { cwd, stdio: "pipe" });
|
|
3151
|
+
return findSyncStashSha(cwd);
|
|
3152
|
+
}
|
|
3153
|
+
function findSyncStashSha(cwd) {
|
|
3154
|
+
try {
|
|
3155
|
+
const line = execFileSync("git", ["stash", "list", "--format=%H %gs"], { cwd, encoding: "utf-8" }).split(`
|
|
3156
|
+
`).find((l) => l.includes(SYNC_STASH_TAG));
|
|
3157
|
+
return line ? line.split(" ")[0] : null;
|
|
3158
|
+
} catch {
|
|
3159
|
+
return null;
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
function stashRefForSha(cwd, sha) {
|
|
3163
|
+
try {
|
|
3164
|
+
const line = execFileSync("git", ["stash", "list", "--format=%H %gd"], { cwd, encoding: "utf-8" }).split(`
|
|
3165
|
+
`).find((l) => l.startsWith(`${sha} `));
|
|
3166
|
+
return line ? line.slice(sha.length + 1).trim() : null;
|
|
3167
|
+
} catch {
|
|
3168
|
+
return null;
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
function restoreSyncStash(cwd, sha) {
|
|
3172
|
+
const resolved = sha ?? findSyncStashSha(cwd);
|
|
3173
|
+
if (!resolved)
|
|
3174
|
+
return "missing";
|
|
3175
|
+
const ref = stashRefForSha(cwd, resolved);
|
|
3176
|
+
if (!ref)
|
|
3177
|
+
return "missing";
|
|
3178
|
+
try {
|
|
3179
|
+
execFileSync("git", ["stash", "apply", resolved], { cwd, stdio: "pipe" });
|
|
3180
|
+
} catch {
|
|
3181
|
+
return "conflict";
|
|
3182
|
+
}
|
|
3183
|
+
const dropRef = stashRefForSha(cwd, resolved);
|
|
3184
|
+
if (dropRef) {
|
|
3185
|
+
try {
|
|
3186
|
+
execFileSync("git", ["stash", "drop", dropRef], { cwd, stdio: "pipe" });
|
|
3187
|
+
} catch {}
|
|
3188
|
+
}
|
|
3189
|
+
return "restored";
|
|
3190
|
+
}
|
|
3099
3191
|
function syncWithRemote(cwd) {
|
|
3100
3192
|
if (!hasCommits(cwd)) {
|
|
3101
3193
|
return { status: "skipped", pushed: false };
|
|
3102
3194
|
}
|
|
3103
3195
|
const dirty = hasTrackedChanges(cwd);
|
|
3196
|
+
let stashSha = null;
|
|
3104
3197
|
if (dirty) {
|
|
3105
|
-
|
|
3198
|
+
stashSha = pushSyncStash(cwd);
|
|
3106
3199
|
}
|
|
3107
3200
|
let status = "synced";
|
|
3108
3201
|
let syncError;
|
|
@@ -3143,9 +3236,8 @@ function syncWithRemote(cwd) {
|
|
|
3143
3236
|
syncError = extractGitError(fetchErr);
|
|
3144
3237
|
}
|
|
3145
3238
|
if (dirty) {
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
} catch {
|
|
3239
|
+
const restored = restoreSyncStash(cwd, stashSha);
|
|
3240
|
+
if (restored !== "restored") {
|
|
3149
3241
|
return { status, pushed: false, error: "stash-conflict", keptUntracked, remoteOverwrote };
|
|
3150
3242
|
}
|
|
3151
3243
|
}
|
|
@@ -3162,6 +3254,7 @@ function syncWithRemote(cwd) {
|
|
|
3162
3254
|
}
|
|
3163
3255
|
return { status, pushed, pushError, keptUntracked, remoteOverwrote };
|
|
3164
3256
|
}
|
|
3257
|
+
var SYNC_STASH_TAG = "runwork-dev-sync";
|
|
3165
3258
|
var init_sync = __esm(() => {
|
|
3166
3259
|
init_subprocess();
|
|
3167
3260
|
init_manifest();
|
|
@@ -5066,10 +5159,6 @@ export declare class FileStorageClient {
|
|
|
5066
5159
|
*/
|
|
5067
5160
|
export declare function createFileStorageClient(env: Env): FileStorageClient;
|
|
5068
5161
|
import type { Hono } from 'hono';
|
|
5069
|
-
/**
|
|
5070
|
-
* Mount file storage routes on the Hono app
|
|
5071
|
-
* Provides REST API for R2 bucket operations
|
|
5072
|
-
*/
|
|
5073
5162
|
export declare function fileStorageRoutes(app: Hono<{
|
|
5074
5163
|
Bindings: Env;
|
|
5075
5164
|
}>): void;
|
|
@@ -5246,6 +5335,46 @@ export declare function componentRoutes(app: Hono<{
|
|
|
5246
5335
|
`,
|
|
5247
5336
|
"workspace.d.ts": `export { WorkspaceContext, getWorkspaceContext, listWorkspaceEntity, getWorkspaceEntity, createWorkspaceEntity, updateWorkspaceEntity, deleteWorkspaceEntity, callAppEndpoint, initializeWorkspace, } from './core-workspace';
|
|
5248
5337
|
export type { WorkspaceUser, ListUsersOptions, ListUsersResponse, NotifyUserParams, NotifyUserResult, ListEntityRequest, ListEntityResponse, GetEntityRequest, CreateEntityRequest, UpdateEntityRequest, DeleteEntityRequest, RegisterEntityRequest, RegisterAppRequest, RegisterComponentRequest, RegisterScheduleRequest, RegisterWorkflowRequest, RegisterEndpointRequest, RegisterIntegrationRequest, CallAppEndpointOptions, CallAppEndpointResult, RegisterAgentRequest as WorkspaceRegisterAgentRequest, } from './core-workspace';
|
|
5338
|
+
`,
|
|
5339
|
+
"client.d.ts": `/**
|
|
5340
|
+
* Framework client entry: browser-side helpers an app's frontend imports as
|
|
5341
|
+
* \`@runworkai/framework/client\`, so app code never carries this plumbing
|
|
5342
|
+
* itself.
|
|
5343
|
+
*
|
|
5344
|
+
* Analytics: what people do in the app, reported to the workspace.
|
|
5345
|
+
*
|
|
5346
|
+
* Envelope follows the Segment Spec (the de-facto standard Google Analytics,
|
|
5347
|
+
* Mixpanel, PostHog and friends all map to), so nothing here needs to change
|
|
5348
|
+
* if the workspace later forwards events to a real analytics tool.
|
|
5349
|
+
*
|
|
5350
|
+
* The app does not interpret events. \`/api/analytics\` stamps the app's
|
|
5351
|
+
* identity and forwards to the workspace, which decides what each one means.
|
|
5352
|
+
* Today a \`page\` counts as one USE of the app, the multiplier behind a
|
|
5353
|
+
* standalone app's value in the workspace's reports. \`track\` events are kept
|
|
5354
|
+
* for a later analytics surface.
|
|
5355
|
+
*
|
|
5356
|
+
* A page load is reported here because the document is served before the
|
|
5357
|
+
* app's Worker runs, leaving no server-side trace otherwise. Once per
|
|
5358
|
+
* document, not per route change: the workspace counts an opened tool, and a
|
|
5359
|
+
* person navigating inside it is still one use.
|
|
5360
|
+
*
|
|
5361
|
+
* Fire-and-forget. Nothing here may ever affect the person using the app.
|
|
5362
|
+
*/
|
|
5363
|
+
/** Record a custom event. Use a flat Title Case name ("Report Exported") and put variables in properties. */
|
|
5364
|
+
export declare function track(event: string, properties?: Record<string, unknown>): void;
|
|
5365
|
+
/** Record a named screen or view inside the app. */
|
|
5366
|
+
export declare function screen(name: string, properties?: Record<string, unknown>): void;
|
|
5367
|
+
/**
|
|
5368
|
+
* Tell the workspace who this browser belongs to, in the APP's own terms.
|
|
5369
|
+
* A claim, recorded as data; the workspace never grants anything on it.
|
|
5370
|
+
*/
|
|
5371
|
+
export declare function identify(userId: string, traits?: Record<string, unknown>): void;
|
|
5372
|
+
/** Attach this person to a company, account or team the app knows about. */
|
|
5373
|
+
export declare function group(groupId: string, traits?: Record<string, unknown>): void;
|
|
5374
|
+
/** Link a past anonymous identity to a known one. */
|
|
5375
|
+
export declare function alias(userId: string, previousId?: string): void;
|
|
5376
|
+
/** Record that this app was opened. Called once automatically on load. */
|
|
5377
|
+
export declare function page(): void;
|
|
5249
5378
|
`,
|
|
5250
5379
|
"core-scheduler.d.ts": `/**
|
|
5251
5380
|
* Core Scheduled Jobs Framework
|
|
@@ -5897,8 +6026,6 @@ export type Doc<T> = {
|
|
|
5897
6026
|
* - Security validation on all field names
|
|
5898
6027
|
*/
|
|
5899
6028
|
export declare class EntityDO extends DurableObject<Env> {
|
|
5900
|
-
ctx: DurableObjectState;
|
|
5901
|
-
env: Env;
|
|
5902
6029
|
private _tableReady;
|
|
5903
6030
|
private _migrationDone;
|
|
5904
6031
|
constructor(ctx: DurableObjectState, env: Env);
|
|
@@ -6903,13 +7030,6 @@ export declare function toChannelName(appName: string): string;
|
|
|
6903
7030
|
* these entries in the observability timeline.
|
|
6904
7031
|
*/
|
|
6905
7032
|
export declare function flog(level: 'info' | 'warn' | 'error', system: string, message: string, data?: Record<string, unknown>): void;
|
|
6906
|
-
/**
|
|
6907
|
-
* Emit an event to the workspace unified event stream.
|
|
6908
|
-
* Fire-and-forget: uses ctx.waitUntil so it doesn't block the response.
|
|
6909
|
-
* Silently skips if workspace env vars are not configured (standalone mode).
|
|
6910
|
-
*
|
|
6911
|
-
* Automatically routes events to a channel derived from APP_NAME when available.
|
|
6912
|
-
*/
|
|
6913
7033
|
export declare function emitEvent(ctx: WaitUntilContext, env: EventEnv, event: EmitEventParams): void;
|
|
6914
7034
|
export {};
|
|
6915
7035
|
`,
|
|
@@ -6984,7 +7104,109 @@ export declare const isStr: (s: unknown) => s is string;
|
|
|
6984
7104
|
* that would cause structured clone to fail in ctx.storage.put().
|
|
6985
7105
|
*/
|
|
6986
7106
|
export declare function safeClone<T>(value: T, fallback?: T): T;
|
|
6987
|
-
|
|
7107
|
+
/**
|
|
7108
|
+
* Platform fetch - routes requests through WorkspaceObject DO for production workers.
|
|
7109
|
+
*
|
|
7110
|
+
* Workers for Platforms (WfP) workers cannot reliably make HTTP requests back to their
|
|
7111
|
+
* parent platform worker (they get 522 timeouts). This utility routes platform API calls
|
|
7112
|
+
* through the WorkspaceObject Durable Object binding, which works across worker boundaries.
|
|
7113
|
+
*
|
|
7114
|
+
* Supported paths:
|
|
7115
|
+
* - /api/proxy/integrations/* - Integration proxy (Nango)
|
|
7116
|
+
* - /api/proxy/openai/* - AI Gateway proxy
|
|
7117
|
+
* - /api/storage/presign - Storage presigned URLs
|
|
7118
|
+
*
|
|
7119
|
+
* For preview containers (DEPLOYMENT_MODE !== 'production'), uses standard fetch.
|
|
7120
|
+
*
|
|
7121
|
+
* @example
|
|
7122
|
+
* \`\`\`typescript
|
|
7123
|
+
* // Instead of:
|
|
7124
|
+
* const response = await fetch('https://runwork.ai/api/proxy/integrations/proxy/contacts', options);
|
|
7125
|
+
*
|
|
7126
|
+
* // Use:
|
|
7127
|
+
* const response = await platformFetch(env, 'https://runwork.ai/api/proxy/integrations/proxy/contacts', options);
|
|
7128
|
+
* \`\`\`
|
|
7129
|
+
*/
|
|
7130
|
+
/**
|
|
7131
|
+
* What the app was doing when it made a call.
|
|
7132
|
+
*
|
|
7133
|
+
* Internal plumbing, deliberately not exported from the package: app authors
|
|
7134
|
+
* never set this, the framework establishes it at each entry point (schedule
|
|
7135
|
+
* tick, workflow run, route handler, agent turn) and \`platformFetch\` below
|
|
7136
|
+
* reads it. It lives beside \`platformFetch\` because that is its only consumer.
|
|
7137
|
+
*
|
|
7138
|
+
* WHY IT EXISTS: usage rows record \`appId\` and \`userId\` and nothing about what
|
|
7139
|
+
* was executing, so a HubSpot call made by a nightly schedule cannot be told
|
|
7140
|
+
* apart from one a person made from their own agent. \`runId\` is the field that
|
|
7141
|
+
* cannot be reconstructed later: it links one run's AI calls and integration
|
|
7142
|
+
* calls together, which is what per-run cost is built from.
|
|
7143
|
+
*
|
|
7144
|
+
* Design: docs/plans/2026-08-28-baselines-capture-redesign.md section 4.1.
|
|
7145
|
+
*/
|
|
7146
|
+
export interface RunContext {
|
|
7147
|
+
/**
|
|
7148
|
+
* WHAT was executing. Only constructs the framework itself runs, never
|
|
7149
|
+
* "where the caller was" (a browser, MCP, the CLI): that is a different
|
|
7150
|
+
* question and \`audit_logs.actor_type\` owns it.
|
|
7151
|
+
*/
|
|
7152
|
+
kind: 'schedule' | 'workflow' | 'endpoint' | 'route' | 'agent';
|
|
7153
|
+
/**
|
|
7154
|
+
* Human-readable name, following the convention the audit rows already use:
|
|
7155
|
+
* a schedule/workflow/agent slug, or \`METHOD /path\` for endpoints and routes.
|
|
7156
|
+
*
|
|
7157
|
+
* OMITTED rather than defaulted when genuinely unknown. A placeholder string
|
|
7158
|
+
* like 'unknown' becomes a value every query has to filter out, and it is
|
|
7159
|
+
* indistinguishable from an app that named something 'unknown'.
|
|
7160
|
+
*/
|
|
7161
|
+
name?: string;
|
|
7162
|
+
/**
|
|
7163
|
+
* App-local registry key, so a usage row joins back to a registration. The
|
|
7164
|
+
* app id is a column of its own, so this is the part after it: a slug for
|
|
7165
|
+
* schedules, workflows and agents, \`METHOD /path\` for endpoints.
|
|
7166
|
+
*/
|
|
7167
|
+
resourceKey?: string;
|
|
7168
|
+
/** One id shared by everything emitted inside this execution. */
|
|
7169
|
+
runId: string;
|
|
7170
|
+
/** The enclosing run, for workflow steps, nested calls and sub-agents. */
|
|
7171
|
+
parentRunId?: string;
|
|
7172
|
+
trigger?: 'cron' | 'manual' | 'webhook' | 'chat' | 'api';
|
|
7173
|
+
/**
|
|
7174
|
+
* The person who started this run. Named to match the governance program's
|
|
7175
|
+
* \`DelegationChain.triggererUserId\` (\`worker/types/permissions.ts\`), which is
|
|
7176
|
+
* the same fact: one name for it across both programs.
|
|
7177
|
+
*
|
|
7178
|
+
* ATTRIBUTION, NEVER AUTHORITY. This travels on a header from the app, behind
|
|
7179
|
+
* the shared WORKSPACE_API_KEY, so an app can put any user id here. The
|
|
7180
|
+
* platform may RECORD it; nothing may ever AUTHORIZE on it. Whose authority a
|
|
7181
|
+
* run borrows is resolved platform-side by PermissionService, not stated by
|
|
7182
|
+
* the caller.
|
|
7183
|
+
*/
|
|
7184
|
+
triggererUserId?: string;
|
|
7185
|
+
attempt?: number;
|
|
7186
|
+
}
|
|
7187
|
+
/** Header carrying the context to the platform side, which writes the audit row. */
|
|
7188
|
+
export declare const RUN_CONTEXT_HEADER = "x-runwork-run-context";
|
|
7189
|
+
/**
|
|
7190
|
+
* Run \`fn\` with \`ctx\` as the ambient execution context.
|
|
7191
|
+
*
|
|
7192
|
+
* Nesting is automatic: entering a context inside another records the outer
|
|
7193
|
+
* \`runId\` as \`parentRunId\` unless the caller set one. That is what makes a
|
|
7194
|
+
* sub-agent's calls point at the run that started it. Re-entering the SAME
|
|
7195
|
+
* run id is not nesting (a workflow resumes into its own run across wake-ups)
|
|
7196
|
+
* and must not make a run its own parent.
|
|
7197
|
+
*/
|
|
7198
|
+
export declare function withRunContext<T>(ctx: RunContext, fn: () => T): T;
|
|
7199
|
+
/** The current execution context, or undefined outside any entry point. */
|
|
7200
|
+
export declare function getRunContext(): RunContext | undefined;
|
|
7201
|
+
/**
|
|
7202
|
+
* Merge the context header into request headers.
|
|
7203
|
+
*
|
|
7204
|
+
* One serialised header rather than several, so adding a field never needs a
|
|
7205
|
+
* matching change in the platform's parsing. Never overwrites a header the
|
|
7206
|
+
* caller set: a caller that knows its own context beats the ambient one.
|
|
7207
|
+
*/
|
|
7208
|
+
export declare function withRunContextHeader(init?: HeadersInit): HeadersInit | undefined;
|
|
7209
|
+
export declare function platformFetch(env: Env, url: string | URL, rawInit?: RequestInit): Promise<Response>;
|
|
6988
7210
|
/**
|
|
6989
7211
|
* Workspace API fetch - routes workspace service requests through WorkspaceObject DO
|
|
6990
7212
|
* for production WfP workers, avoiding 522 recursive invocation errors.
|
|
@@ -6997,7 +7219,7 @@ export declare function platformFetch(env: Env, url: string | URL, init?: Reques
|
|
|
6997
7219
|
* @param path - Workspace API sub-path (e.g., '/ingest-event')
|
|
6998
7220
|
* @param init - Standard fetch options
|
|
6999
7221
|
*/
|
|
7000
|
-
export declare function workspaceApiFetch(env: Pick<Env, 'WORKSPACE_API_URL' | 'WORKSPACE_API_KEY' | 'WORKSPACE_ID' | 'DEPLOYMENT_MODE' | 'WorkspaceObject'>, path: string,
|
|
7222
|
+
export declare function workspaceApiFetch(env: Pick<Env, 'WORKSPACE_API_URL' | 'WORKSPACE_API_KEY' | 'WORKSPACE_ID' | 'DEPLOYMENT_MODE' | 'WorkspaceObject'>, path: string, rawInit?: RequestInit): Promise<Response>;
|
|
7001
7223
|
`,
|
|
7002
7224
|
"workflows.d.ts": `import type { WorkflowDefinition } from './core-workflow-types';
|
|
7003
7225
|
/**
|
|
@@ -7192,7 +7414,7 @@ export declare class BaseAgent extends AIChatAgent<Env> {
|
|
|
7192
7414
|
* - Types are defined in core-workflow-types.ts
|
|
7193
7415
|
* - Native DO-based implementation is in core-workflow-instance.ts and core-workflow-coordinator.ts
|
|
7194
7416
|
*/
|
|
7195
|
-
import type
|
|
7417
|
+
import { type Env } from './core-utils';
|
|
7196
7418
|
export type { WorkflowResult, WorkflowStatus, WorkflowInstanceInfo, WorkflowDefinition, WorkflowContext, WorkflowStepUtilities, StepOptions, WaitEventOptions, WorkflowLogger, WorkflowState, WorkflowStatusResponse, } from './core-workflow-types';
|
|
7197
7419
|
import type { WorkflowInstanceInfo } from './core-workflow-types';
|
|
7198
7420
|
/**
|
|
@@ -7741,8 +7963,8 @@ export declare const integrationApiSchema: z.ZodObject<{
|
|
|
7741
7963
|
endpoint: z.ZodString;
|
|
7742
7964
|
data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
7743
7965
|
}, "strip", z.ZodTypeAny, {
|
|
7744
|
-
method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE";
|
|
7745
7966
|
endpoint: string;
|
|
7967
|
+
method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE";
|
|
7746
7968
|
data?: Record<string, unknown> | undefined;
|
|
7747
7969
|
}, {
|
|
7748
7970
|
endpoint: string;
|
|
@@ -7983,7 +8205,7 @@ function createKeyboardListener() {
|
|
|
7983
8205
|
}
|
|
7984
8206
|
|
|
7985
8207
|
// src/generated/version.ts
|
|
7986
|
-
var VERSION = "0.
|
|
8208
|
+
var VERSION = "0.26.0";
|
|
7987
8209
|
|
|
7988
8210
|
// src/commands/dev.ts
|
|
7989
8211
|
var exports_dev = {};
|
|
@@ -8064,6 +8286,7 @@ async function execDev(options) {
|
|
|
8064
8286
|
await ensureGitCredentialHelper(creds.baseUrl);
|
|
8065
8287
|
}
|
|
8066
8288
|
ensureRunworkRemote(cwd, client.getGitRemoteUrl(config.workspaceId, config.appId));
|
|
8289
|
+
let editsUnprotected = false;
|
|
8067
8290
|
const oldManifest = await loadManifest(cwd);
|
|
8068
8291
|
if (oldManifest) {
|
|
8069
8292
|
const userEdits = await detectUserEdits(cwd, oldManifest);
|
|
@@ -8087,9 +8310,22 @@ async function execDev(options) {
|
|
|
8087
8310
|
execFileSync("git", ["add", "--", ...userEdits], { stdio: "pipe" });
|
|
8088
8311
|
execFileSync("git", ["commit", "-m", "sync: user edits"], { stdio: "pipe" });
|
|
8089
8312
|
} catch {}
|
|
8313
|
+
editsUnprotected = hasTrackedChanges(cwd);
|
|
8314
|
+
if (editsUnprotected) {
|
|
8315
|
+
if (useJson) {
|
|
8316
|
+
jsonLine({
|
|
8317
|
+
event: "startup",
|
|
8318
|
+
phase: "user_edits_unprotected",
|
|
8319
|
+
timestamp: ts(),
|
|
8320
|
+
warning: "Could not commit your edits (a commit hook or lock may be interfering). Skipping the template update so they are not lost."
|
|
8321
|
+
});
|
|
8322
|
+
} else {
|
|
8323
|
+
console.warn(yellow("Could not commit your edits (a commit hook or lock may be interfering). Skipping the template update so they are not lost."));
|
|
8324
|
+
}
|
|
8325
|
+
}
|
|
8090
8326
|
}
|
|
8091
8327
|
}
|
|
8092
|
-
if (!noSync) {
|
|
8328
|
+
if (!noSync && !editsUnprotected) {
|
|
8093
8329
|
if (useJson) {
|
|
8094
8330
|
jsonLine({ event: "startup", phase: "template_update", timestamp: ts() });
|
|
8095
8331
|
} else {
|
|
@@ -8912,6 +9148,53 @@ var init_dev = __esm(() => {
|
|
|
8912
9148
|
devCommand.addCommand(devAttachCommand);
|
|
8913
9149
|
});
|
|
8914
9150
|
|
|
9151
|
+
// src/agents/detection-probes.ts
|
|
9152
|
+
function powershellQuote(value) {
|
|
9153
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
9154
|
+
}
|
|
9155
|
+
function isValidBundleId(id) {
|
|
9156
|
+
return /^[A-Za-z0-9][A-Za-z0-9.-]*$/.test(id);
|
|
9157
|
+
}
|
|
9158
|
+
function macosBundleIdProbeScript(id) {
|
|
9159
|
+
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`;
|
|
9160
|
+
}
|
|
9161
|
+
function appxPackageProbeScript(pkg) {
|
|
9162
|
+
return `$p = Get-AppxPackage -Name ${powershellQuote(pkg)} -ErrorAction SilentlyContinue; if ($null -ne $p) { exit 0 } exit 1`;
|
|
9163
|
+
}
|
|
9164
|
+
function startAppProbeScript(pattern) {
|
|
9165
|
+
const p = powershellQuote(pattern);
|
|
9166
|
+
return `$a = Get-StartApps -ErrorAction SilentlyContinue | Where-Object { $_.Name -like ${p} -or $_.AppID -like ${p} } | Select-Object -First 1; if ($null -ne $a) { exit 0 } exit 1`;
|
|
9167
|
+
}
|
|
9168
|
+
function isCommandNotFoundExit(code) {
|
|
9169
|
+
return typeof code === "number" && COMMAND_NOT_FOUND_EXIT_CODES.includes(code);
|
|
9170
|
+
}
|
|
9171
|
+
function expandWindowsPathTemplate(path2, resolved) {
|
|
9172
|
+
for (const [placeholder, defaults] of Object.entries(WINDOWS_PATH_PLACEHOLDERS)) {
|
|
9173
|
+
if (!path2.startsWith(placeholder))
|
|
9174
|
+
continue;
|
|
9175
|
+
const rest = path2.slice(placeholder.length).replace(/^[/\\]/, "");
|
|
9176
|
+
const fromEnv = resolved[placeholder];
|
|
9177
|
+
if (fromEnv) {
|
|
9178
|
+
return { path: `${fromEnv.replace(/[/\\]+$/, "")}\\${rest.replace(/\//g, "\\")}`, needsHomeJoin: false };
|
|
9179
|
+
}
|
|
9180
|
+
if ("absoluteDefault" in defaults) {
|
|
9181
|
+
return { path: `${defaults.absoluteDefault}\\${rest.replace(/\//g, "\\")}`, needsHomeJoin: false };
|
|
9182
|
+
}
|
|
9183
|
+
return { path: `${defaults.homeRelativeDefault}/${rest}`, needsHomeJoin: true };
|
|
9184
|
+
}
|
|
9185
|
+
return { path: path2, needsHomeJoin: !/^([A-Za-z]:[\\/]|\\\\|\/)/.test(path2) };
|
|
9186
|
+
}
|
|
9187
|
+
var PATH_REFRESH_FAILED_MARKER = "__runwork_path_refresh_failed__", WINDOWS_PATH_REFRESH, COMMAND_NOT_FOUND_EXIT_CODES, WINDOWS_PATH_PLACEHOLDERS;
|
|
9188
|
+
var init_detection_probes = __esm(() => {
|
|
9189
|
+
WINDOWS_PATH_REFRESH = "try { $env:Path = [Environment]::GetEnvironmentVariable('Path','Machine') + ';' + " + "[Environment]::GetEnvironmentVariable('Path','User') + ';' + $env:Path } " + `catch { Write-Output '${PATH_REFRESH_FAILED_MARKER}' }; `;
|
|
9190
|
+
COMMAND_NOT_FOUND_EXIT_CODES = [127, 9009];
|
|
9191
|
+
WINDOWS_PATH_PLACEHOLDERS = {
|
|
9192
|
+
"%APPDATA%": { homeRelativeDefault: "AppData/Roaming" },
|
|
9193
|
+
"%LOCALAPPDATA%": { homeRelativeDefault: "AppData/Local" },
|
|
9194
|
+
"%ProgramFiles%": { absoluteDefault: "C:\\Program Files" }
|
|
9195
|
+
};
|
|
9196
|
+
});
|
|
9197
|
+
|
|
8915
9198
|
// src/utils/which.ts
|
|
8916
9199
|
import { platform } from "os";
|
|
8917
9200
|
import { isAbsolute } from "path";
|
|
@@ -8932,11 +9215,25 @@ function whichAllLines(name) {
|
|
|
8932
9215
|
return [];
|
|
8933
9216
|
}
|
|
8934
9217
|
}
|
|
8935
|
-
function
|
|
9218
|
+
function classifyProbeError(err) {
|
|
9219
|
+
const e = err;
|
|
9220
|
+
if (!e || typeof e !== "object")
|
|
9221
|
+
return "spawn-error";
|
|
9222
|
+
if (e.signal || e.code === "ETIMEDOUT")
|
|
9223
|
+
return "timeout";
|
|
9224
|
+
if (e.code === "ENOENT")
|
|
9225
|
+
return "not-found";
|
|
9226
|
+
if (isCommandNotFoundExit(e.status))
|
|
9227
|
+
return "not-found";
|
|
9228
|
+
if (typeof e.status === "number")
|
|
9229
|
+
return "exit-nonzero";
|
|
9230
|
+
return "spawn-error";
|
|
9231
|
+
}
|
|
9232
|
+
function probeBinaryRunnable(binary) {
|
|
8936
9233
|
const cached = runnableCache.get(binary);
|
|
8937
9234
|
if (cached !== undefined)
|
|
8938
9235
|
return cached;
|
|
8939
|
-
let
|
|
9236
|
+
let probe;
|
|
8940
9237
|
try {
|
|
8941
9238
|
const spec = toSpawnSpec(binary, ["--version"]);
|
|
8942
9239
|
execFileSync(spec.command, spec.args, {
|
|
@@ -8944,12 +9241,16 @@ function isBinaryRunnable(binary) {
|
|
|
8944
9241
|
timeout: RUNNABLE_PROBE_TIMEOUT_MS,
|
|
8945
9242
|
...spec.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
8946
9243
|
});
|
|
8947
|
-
|
|
8948
|
-
} catch {
|
|
8949
|
-
|
|
9244
|
+
probe = { runnable: true, reason: "ran" };
|
|
9245
|
+
} catch (err) {
|
|
9246
|
+
probe = { runnable: false, reason: classifyProbeError(err) };
|
|
8950
9247
|
}
|
|
8951
|
-
|
|
8952
|
-
|
|
9248
|
+
if (probe.reason !== "timeout")
|
|
9249
|
+
runnableCache.set(binary, probe);
|
|
9250
|
+
return probe;
|
|
9251
|
+
}
|
|
9252
|
+
function isBinaryRunnable(binary) {
|
|
9253
|
+
return probeBinaryRunnable(binary).runnable;
|
|
8953
9254
|
}
|
|
8954
9255
|
function toSpawnSpec(binary, args) {
|
|
8955
9256
|
if (isAbsolute(binary)) {
|
|
@@ -8997,6 +9298,7 @@ function isAppRunning(appName) {
|
|
|
8997
9298
|
var RUNNABLE_PROBE_TIMEOUT_MS = 1e4, runnableCache;
|
|
8998
9299
|
var init_which = __esm(() => {
|
|
8999
9300
|
init_subprocess();
|
|
9301
|
+
init_detection_probes();
|
|
9000
9302
|
runnableCache = new Map;
|
|
9001
9303
|
});
|
|
9002
9304
|
|
|
@@ -9393,7 +9695,7 @@ var init_registry_data = __esm(() => {
|
|
|
9393
9695
|
{
|
|
9394
9696
|
method: "path",
|
|
9395
9697
|
target: {
|
|
9396
|
-
windows: "
|
|
9698
|
+
windows: "%APPDATA%/Claude/claude_desktop_config.json",
|
|
9397
9699
|
linux: ".config/Claude/claude_desktop_config.json"
|
|
9398
9700
|
}
|
|
9399
9701
|
}
|
|
@@ -9406,7 +9708,7 @@ var init_registry_data = __esm(() => {
|
|
|
9406
9708
|
skillsPaths: { global: ".claude/skills", project: ".claude/skills" },
|
|
9407
9709
|
mcpConfigPath: {
|
|
9408
9710
|
macos: "Library/Application Support/Claude/claude_desktop_config.json",
|
|
9409
|
-
windows: "
|
|
9711
|
+
windows: "%APPDATA%/Claude/claude_desktop_config.json",
|
|
9410
9712
|
linux: ".config/Claude/claude_desktop_config.json"
|
|
9411
9713
|
},
|
|
9412
9714
|
mcpConfigKey: "mcpServers",
|
|
@@ -9716,8 +10018,8 @@ var init_registry_data = __esm(() => {
|
|
|
9716
10018
|
target: [
|
|
9717
10019
|
{ method: "binary", target: "windsurf" },
|
|
9718
10020
|
{ method: "path", target: { macos: "/Applications/Windsurf.app" } },
|
|
9719
|
-
{ method: "path", target: { windows: "
|
|
9720
|
-
{ method: "path", target: { windows: "
|
|
10021
|
+
{ method: "path", target: { windows: "%LOCALAPPDATA%/Programs/Windsurf" } },
|
|
10022
|
+
{ method: "path", target: { windows: "%ProgramFiles%/Windsurf" } }
|
|
9721
10023
|
]
|
|
9722
10024
|
},
|
|
9723
10025
|
launch: { app: { macos: "Windsurf", windows: "Windsurf" } },
|
|
@@ -9797,7 +10099,14 @@ var init_registry_data = __esm(() => {
|
|
|
9797
10099
|
name: "GitHub Copilot (VS Code)",
|
|
9798
10100
|
description: "GitHub's AI pair programmer in VS Code",
|
|
9799
10101
|
category: "extension",
|
|
9800
|
-
detection: {
|
|
10102
|
+
detection: {
|
|
10103
|
+
method: "path",
|
|
10104
|
+
target: {
|
|
10105
|
+
macos: "Library/Application Support/Code/User/globalStorage/github.copilot",
|
|
10106
|
+
windows: "%APPDATA%/Code/User/globalStorage/github.copilot",
|
|
10107
|
+
linux: ".config/Code/User/globalStorage/github.copilot"
|
|
10108
|
+
}
|
|
10109
|
+
},
|
|
9801
10110
|
launch: { app: { macos: "Visual Studio Code", windows: "Code" }, cli: "code" },
|
|
9802
10111
|
logo: "vscode",
|
|
9803
10112
|
downloadUrl: "https://marketplace.visualstudio.com/items?itemName=GitHub.copilot",
|
|
@@ -9820,7 +10129,7 @@ var init_registry_data = __esm(() => {
|
|
|
9820
10129
|
name: "GitHub Copilot CLI",
|
|
9821
10130
|
description: "GitHub Copilot in the terminal",
|
|
9822
10131
|
category: "cli",
|
|
9823
|
-
detection: { method: "binary", target: "
|
|
10132
|
+
detection: { method: "binary", target: "copilot" },
|
|
9824
10133
|
logo: "vscode",
|
|
9825
10134
|
skillsPaths: { global: ".copilot/skills", project: ".github/skills" },
|
|
9826
10135
|
mcpConfigPath: ".copilot/mcp-config.json",
|
|
@@ -9841,7 +10150,7 @@ var init_registry_data = __esm(() => {
|
|
|
9841
10150
|
{ slug: "droid", name: "Droid", aliases: ["Droid (Factory AI)"], description: "Factory AI's coding agent", category: "cli", detection: { method: "binary", target: "droid" }, skillsPaths: { global: ".factory/skills", project: ".factory/skills" } },
|
|
9842
10151
|
{ slug: "firebender", name: "Firebender", description: "AI coding agent", category: "cli", detection: { method: "binary", target: "firebender" }, skillsPaths: { global: ".firebender/skills", project: ".firebender/skills" } },
|
|
9843
10152
|
{ slug: "goose", name: "Goose", description: "AI coding agent by Block", category: "cli", detection: { method: "binary", target: "goose" }, skillsPaths: { global: ".config/goose/skills", project: ".goose/skills" } },
|
|
9844
|
-
{ slug: "hermes", name: "Hermes", aliases: ["hermes-agent"], description: "AI coding agent", category: "cli", detection: { method: "binary", target: "hermes" }, skillsPaths: { global: ".hermes/skills", project: ".hermes/skills" } },
|
|
10153
|
+
{ slug: "hermes", name: "Hermes", aliases: ["hermes-agent"], description: "AI coding agent", category: "cli", detection: { method: "binary", target: "hermes" }, launch: { cli: "hermes" }, skillsPaths: { global: ".hermes/skills", project: ".hermes/skills" } },
|
|
9845
10154
|
{ slug: "iflow", name: "iFlow CLI", aliases: ["iflow-cli"], description: "AI coding agent", category: "cli", detection: { method: "binary", target: "iflow" }, skillsPaths: { global: ".iflow/skills", project: ".iflow/skills" } },
|
|
9846
10155
|
{ slug: "junie", name: "Junie", description: "JetBrains AI coding agent", category: "ide", detection: { method: "binary", target: "junie" }, skillsPaths: { global: ".junie/skills", project: ".junie/skills" } },
|
|
9847
10156
|
{ slug: "kilocode", name: "Kilo Code", aliases: ["kilo"], description: "AI coding agent", category: "extension", detection: { method: "binary", target: "kilocode" }, skillsPaths: { global: ".kilocode/skills", project: ".kilocode/skills" } },
|
|
@@ -11034,11 +11343,33 @@ function readJsonConfig(filePath) {
|
|
|
11034
11343
|
return {};
|
|
11035
11344
|
try {
|
|
11036
11345
|
return JSON.parse(readFileSync23(filePath, "utf-8"));
|
|
11037
|
-
} catch {
|
|
11346
|
+
} catch (err) {
|
|
11347
|
+
console.warn(` [config] ${filePath} is not valid JSON: ${err instanceof Error ? err.message : err}`);
|
|
11038
11348
|
return {};
|
|
11039
11349
|
}
|
|
11040
11350
|
}
|
|
11351
|
+
function existingContentIsUnparseable(filePath) {
|
|
11352
|
+
if (!existsSync28(filePath))
|
|
11353
|
+
return { bad: false };
|
|
11354
|
+
let raw;
|
|
11355
|
+
try {
|
|
11356
|
+
raw = readFileSync23(filePath, "utf-8");
|
|
11357
|
+
} catch {
|
|
11358
|
+
return { bad: false };
|
|
11359
|
+
}
|
|
11360
|
+
if (raw.trim() === "")
|
|
11361
|
+
return { bad: false };
|
|
11362
|
+
try {
|
|
11363
|
+
JSON.parse(raw);
|
|
11364
|
+
return { bad: false };
|
|
11365
|
+
} catch (cause) {
|
|
11366
|
+
return { bad: true, cause };
|
|
11367
|
+
}
|
|
11368
|
+
}
|
|
11041
11369
|
function writeJsonConfig(filePath, config) {
|
|
11370
|
+
const { bad, cause } = existingContentIsUnparseable(filePath);
|
|
11371
|
+
if (bad)
|
|
11372
|
+
throw new ConfigParseError(filePath, cause);
|
|
11042
11373
|
mkdirSync14(dirname7(filePath), { recursive: true });
|
|
11043
11374
|
writeFileSync14(filePath, JSON.stringify(config, null, 2) + `
|
|
11044
11375
|
`);
|
|
@@ -11082,9 +11413,20 @@ function mergeJsonMcpServers(filePath, servers, topKey) {
|
|
|
11082
11413
|
writeJsonConfig(filePath, config);
|
|
11083
11414
|
return true;
|
|
11084
11415
|
}
|
|
11416
|
+
var ConfigParseError;
|
|
11085
11417
|
var init_json_config = __esm(() => {
|
|
11086
11418
|
init_types();
|
|
11087
11419
|
init_hash();
|
|
11420
|
+
ConfigParseError = class ConfigParseError extends Error {
|
|
11421
|
+
filePath;
|
|
11422
|
+
cause;
|
|
11423
|
+
constructor(filePath, cause) {
|
|
11424
|
+
super(`${filePath} is not valid JSON, so Runwork left it untouched. ` + `Fix the file (or move it aside) and run the command again.`);
|
|
11425
|
+
this.filePath = filePath;
|
|
11426
|
+
this.cause = cause;
|
|
11427
|
+
this.name = "ConfigParseError";
|
|
11428
|
+
}
|
|
11429
|
+
};
|
|
11088
11430
|
});
|
|
11089
11431
|
|
|
11090
11432
|
// src/agents/utils/skill-removal.ts
|
|
@@ -11639,29 +11981,11 @@ function vlog(...args) {
|
|
|
11639
11981
|
}
|
|
11640
11982
|
var verbose = false;
|
|
11641
11983
|
|
|
11642
|
-
// src/agents/detection-probes.ts
|
|
11643
|
-
function powershellQuote(value) {
|
|
11644
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
11645
|
-
}
|
|
11646
|
-
function isValidBundleId(id) {
|
|
11647
|
-
return /^[A-Za-z0-9][A-Za-z0-9.-]*$/.test(id);
|
|
11648
|
-
}
|
|
11649
|
-
function macosBundleIdProbeScript(id) {
|
|
11650
|
-
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`;
|
|
11651
|
-
}
|
|
11652
|
-
function appxPackageProbeScript(pkg) {
|
|
11653
|
-
return `$p = Get-AppxPackage -Name ${powershellQuote(pkg)} -ErrorAction SilentlyContinue; if ($null -ne $p) { exit 0 } exit 1`;
|
|
11654
|
-
}
|
|
11655
|
-
function startAppProbeScript(pattern) {
|
|
11656
|
-
const p = powershellQuote(pattern);
|
|
11657
|
-
return `$a = Get-StartApps -ErrorAction SilentlyContinue | Where-Object { $_.Name -like ${p} -or $_.AppID -like ${p} } | Select-Object -First 1; if ($null -ne $a) { exit 0 } exit 1`;
|
|
11658
|
-
}
|
|
11659
|
-
|
|
11660
11984
|
// src/agents/detection.ts
|
|
11661
11985
|
import { execFile } from "child_process";
|
|
11662
11986
|
import { existsSync as existsSync31 } from "fs";
|
|
11663
11987
|
import { homedir as homedir8, platform as platform3 } from "os";
|
|
11664
|
-
import {
|
|
11988
|
+
import { join as join25 } from "path";
|
|
11665
11989
|
import { promisify } from "util";
|
|
11666
11990
|
function isWindows() {
|
|
11667
11991
|
return platform3() === "win32";
|
|
@@ -11682,7 +12006,12 @@ function resolveDetectionPath(target) {
|
|
|
11682
12006
|
const resolved = resolvePlatformString(target);
|
|
11683
12007
|
if (!resolved)
|
|
11684
12008
|
return null;
|
|
11685
|
-
|
|
12009
|
+
const expanded = expandWindowsPathTemplate(resolved, {
|
|
12010
|
+
"%APPDATA%": process.env.APPDATA,
|
|
12011
|
+
"%LOCALAPPDATA%": process.env.LOCALAPPDATA,
|
|
12012
|
+
"%ProgramFiles%": process.env.ProgramFiles
|
|
12013
|
+
});
|
|
12014
|
+
return expanded.needsHomeJoin ? join25(homedir8(), expanded.path) : expanded.path;
|
|
11686
12015
|
}
|
|
11687
12016
|
function checkPath(target) {
|
|
11688
12017
|
const absolute = resolveDetectionPath(target);
|
|
@@ -11762,11 +12091,74 @@ var init_detection = __esm(() => {
|
|
|
11762
12091
|
init_which();
|
|
11763
12092
|
init_registry_data();
|
|
11764
12093
|
init_registry();
|
|
12094
|
+
init_detection_probes();
|
|
11765
12095
|
NOT_DETECTED = { detected: false };
|
|
11766
12096
|
});
|
|
11767
12097
|
|
|
12098
|
+
// src/agents/transcript-sources.ts
|
|
12099
|
+
import { accessSync, constants, statSync as statSync4 } from "fs";
|
|
12100
|
+
function classifyFsErrorCode(code) {
|
|
12101
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
12102
|
+
return "missing-dir";
|
|
12103
|
+
if (code === "EACCES" || code === "EPERM")
|
|
12104
|
+
return "permission-denied";
|
|
12105
|
+
return "error";
|
|
12106
|
+
}
|
|
12107
|
+
function errorCode(err) {
|
|
12108
|
+
const e = err;
|
|
12109
|
+
return e && typeof e === "object" && typeof e.code === "string" ? e.code : undefined;
|
|
12110
|
+
}
|
|
12111
|
+
function probeTranscriptSource(source) {
|
|
12112
|
+
if (source.available && !source.available()) {
|
|
12113
|
+
return { status: "missing-tool", path: source.path, detail: source.tool ?? "unknown tool" };
|
|
12114
|
+
}
|
|
12115
|
+
try {
|
|
12116
|
+
statSync4(source.path);
|
|
12117
|
+
} catch (err) {
|
|
12118
|
+
return { status: classifyFsErrorCode(errorCode(err)), path: source.path, detail: errorCode(err) };
|
|
12119
|
+
}
|
|
12120
|
+
try {
|
|
12121
|
+
accessSync(source.path, source.kind === "dir" ? constants.R_OK | constants.X_OK : constants.R_OK);
|
|
12122
|
+
} catch (err) {
|
|
12123
|
+
return { status: classifyFsErrorCode(errorCode(err)), path: source.path, detail: errorCode(err) };
|
|
12124
|
+
}
|
|
12125
|
+
return { status: "ok", path: source.path };
|
|
12126
|
+
}
|
|
12127
|
+
function diagnoseTranscriptRead(agent, thrown) {
|
|
12128
|
+
const probe = summarizeTranscriptSources(agent.transcriptSources?.() ?? []);
|
|
12129
|
+
const base = { agentSlug: agent.slug, sessions: null };
|
|
12130
|
+
if (probe.status !== "ok" && probe.status !== "no-reader") {
|
|
12131
|
+
return {
|
|
12132
|
+
...base,
|
|
12133
|
+
status: probe.status,
|
|
12134
|
+
path: probe.path,
|
|
12135
|
+
...probe.detail ? { detail: probe.detail } : {}
|
|
12136
|
+
};
|
|
12137
|
+
}
|
|
12138
|
+
if (thrown !== undefined) {
|
|
12139
|
+
return { ...base, status: "error", detail: thrown instanceof Error ? thrown.message : String(thrown) };
|
|
12140
|
+
}
|
|
12141
|
+
return probe.status === "ok" ? { ...base, status: "error", path: probe.path, detail: "read returned no result" } : { ...base, status: "no-reader" };
|
|
12142
|
+
}
|
|
12143
|
+
function transcriptSourcesReadable(sources) {
|
|
12144
|
+
return summarizeTranscriptSources(sources).status === "ok";
|
|
12145
|
+
}
|
|
12146
|
+
function summarizeTranscriptSources(sources) {
|
|
12147
|
+
if (sources.length === 0)
|
|
12148
|
+
return { status: "no-reader", path: "" };
|
|
12149
|
+
const probes = sources.map(probeTranscriptSource);
|
|
12150
|
+
const rank = ["permission-denied", "missing-tool", "error", "ok", "missing-dir"];
|
|
12151
|
+
for (const status of rank) {
|
|
12152
|
+
const hit = probes.find((p) => p.status === status);
|
|
12153
|
+
if (hit)
|
|
12154
|
+
return hit;
|
|
12155
|
+
}
|
|
12156
|
+
return probes[0];
|
|
12157
|
+
}
|
|
12158
|
+
var init_transcript_sources = () => {};
|
|
12159
|
+
|
|
11768
12160
|
// src/agents/claude-code.ts
|
|
11769
|
-
import { chmodSync, existsSync as existsSync32, mkdirSync as mkdirSync16, readFileSync as readFileSync25, readdirSync as readdirSync7, rmSync as rmSync5, statSync as
|
|
12161
|
+
import { chmodSync, existsSync as existsSync32, mkdirSync as mkdirSync16, readFileSync as readFileSync25, readdirSync as readdirSync7, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync16 } from "fs";
|
|
11770
12162
|
import { join as join26 } from "path";
|
|
11771
12163
|
import { homedir as homedir9 } from "os";
|
|
11772
12164
|
function getPluginJson() {
|
|
@@ -11810,6 +12202,7 @@ var init_claude_code = __esm(() => {
|
|
|
11810
12202
|
init_instruction_hint();
|
|
11811
12203
|
init_session_start_hook();
|
|
11812
12204
|
init_detection();
|
|
12205
|
+
init_transcript_sources();
|
|
11813
12206
|
ClaudeCodeAdapter = class ClaudeCodeAdapter extends RegistryDetectedAdapter {
|
|
11814
12207
|
name = "Claude Code";
|
|
11815
12208
|
slug = "claude-code";
|
|
@@ -11986,8 +12379,7 @@ ${instructions}`;
|
|
|
11986
12379
|
}
|
|
11987
12380
|
if (!hadFile && !config.modelPreference && !config.permissionRules)
|
|
11988
12381
|
return;
|
|
11989
|
-
|
|
11990
|
-
writeFileSync16(settingsPath, JSON.stringify(settings, null, 2));
|
|
12382
|
+
writeJsonConfig(settingsPath, settings);
|
|
11991
12383
|
}
|
|
11992
12384
|
async readManagedBlock(_scope) {
|
|
11993
12385
|
return;
|
|
@@ -12034,7 +12426,7 @@ ${instructions}`;
|
|
|
12034
12426
|
delete settings.enabledPlugins[key];
|
|
12035
12427
|
}
|
|
12036
12428
|
}
|
|
12037
|
-
|
|
12429
|
+
writeJsonConfig(settingsPath, settings);
|
|
12038
12430
|
} catch {}
|
|
12039
12431
|
}
|
|
12040
12432
|
const pluginDir = this.getPluginDir();
|
|
@@ -12108,7 +12500,7 @@ ${instructions}`;
|
|
|
12108
12500
|
const filePath = join26(cwdPath, file);
|
|
12109
12501
|
let stat;
|
|
12110
12502
|
try {
|
|
12111
|
-
stat =
|
|
12503
|
+
stat = statSync5(filePath);
|
|
12112
12504
|
} catch {
|
|
12113
12505
|
continue;
|
|
12114
12506
|
}
|
|
@@ -12200,9 +12592,15 @@ ${instructions}`;
|
|
|
12200
12592
|
return null;
|
|
12201
12593
|
}
|
|
12202
12594
|
}
|
|
12595
|
+
transcriptRoot() {
|
|
12596
|
+
return join26(homedir9(), ".claude", "projects");
|
|
12597
|
+
}
|
|
12598
|
+
transcriptSources() {
|
|
12599
|
+
return [{ path: this.transcriptRoot(), kind: "dir" }];
|
|
12600
|
+
}
|
|
12203
12601
|
async readSessionDigests(sinceISO) {
|
|
12204
12602
|
try {
|
|
12205
|
-
const projectsDir =
|
|
12603
|
+
const projectsDir = this.transcriptRoot();
|
|
12206
12604
|
if (!existsSync32(projectsDir))
|
|
12207
12605
|
return null;
|
|
12208
12606
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
@@ -12227,7 +12625,7 @@ ${instructions}`;
|
|
|
12227
12625
|
const filePath = join26(cwdPath, file);
|
|
12228
12626
|
let stat;
|
|
12229
12627
|
try {
|
|
12230
|
-
stat =
|
|
12628
|
+
stat = statSync5(filePath);
|
|
12231
12629
|
} catch {
|
|
12232
12630
|
continue;
|
|
12233
12631
|
}
|
|
@@ -12251,8 +12649,8 @@ ${instructions}`;
|
|
|
12251
12649
|
}
|
|
12252
12650
|
async listSessions(sinceISO) {
|
|
12253
12651
|
try {
|
|
12254
|
-
const projectsDir =
|
|
12255
|
-
if (!
|
|
12652
|
+
const projectsDir = this.transcriptRoot();
|
|
12653
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
12256
12654
|
return null;
|
|
12257
12655
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
12258
12656
|
let cwdEntries;
|
|
@@ -12276,7 +12674,7 @@ ${instructions}`;
|
|
|
12276
12674
|
const filePath = join26(cwdPath, file);
|
|
12277
12675
|
let stat;
|
|
12278
12676
|
try {
|
|
12279
|
-
stat =
|
|
12677
|
+
stat = statSync5(filePath);
|
|
12280
12678
|
} catch {
|
|
12281
12679
|
continue;
|
|
12282
12680
|
}
|
|
@@ -12314,7 +12712,7 @@ ${instructions}`;
|
|
|
12314
12712
|
}
|
|
12315
12713
|
async readSkillUsage(lastSyncAt) {
|
|
12316
12714
|
try {
|
|
12317
|
-
const projectsDir =
|
|
12715
|
+
const projectsDir = this.transcriptRoot();
|
|
12318
12716
|
if (!existsSync32(projectsDir))
|
|
12319
12717
|
return null;
|
|
12320
12718
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -12349,7 +12747,7 @@ ${instructions}`;
|
|
|
12349
12747
|
const filePath = join26(cwdPath, file);
|
|
12350
12748
|
let fileStat;
|
|
12351
12749
|
try {
|
|
12352
|
-
fileStat =
|
|
12750
|
+
fileStat = statSync5(filePath);
|
|
12353
12751
|
} catch {
|
|
12354
12752
|
continue;
|
|
12355
12753
|
}
|
|
@@ -12605,7 +13003,7 @@ var init_claude_desktop_plugin_tree = __esm(() => {
|
|
|
12605
13003
|
});
|
|
12606
13004
|
|
|
12607
13005
|
// src/agents/claude-desktop.ts
|
|
12608
|
-
import { existsSync as existsSync34, mkdtempSync as mkdtempSync3, readdirSync as readdirSync8, readFileSync as readFileSync26, rmSync as rmSync7, statSync as
|
|
13006
|
+
import { existsSync as existsSync34, mkdtempSync as mkdtempSync3, readdirSync as readdirSync8, readFileSync as readFileSync26, rmSync as rmSync7, statSync as statSync6, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18 } from "fs";
|
|
12609
13007
|
import { dirname as dirname9, join as join28 } from "path";
|
|
12610
13008
|
import { homedir as homedir10, platform as platform4, tmpdir as tmpdir3 } from "os";
|
|
12611
13009
|
function isRunworkRpmPluginName(name) {
|
|
@@ -12764,6 +13162,7 @@ function setCoworkPluginEnabled(pluginsDir, enabled) {
|
|
|
12764
13162
|
var PLUGIN_NAME2 = "runwork", PLUGIN_VERSION2 = "1.0.0", PLUGIN_DESCRIPTION = "Skills and tools from your Runwork workspace", PLUGIN_AUTHOR_NAME = "Runwork", ClaudeDesktopAdapter;
|
|
12765
13163
|
var init_claude_desktop = __esm(() => {
|
|
12766
13164
|
init_types();
|
|
13165
|
+
init_transcript_sources();
|
|
12767
13166
|
init_session_digest();
|
|
12768
13167
|
init_json_config();
|
|
12769
13168
|
init_instruction_hint();
|
|
@@ -12926,7 +13325,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
12926
13325
|
prefs.ccdScheduledTasksEnabled = false;
|
|
12927
13326
|
}
|
|
12928
13327
|
}
|
|
12929
|
-
|
|
13328
|
+
writeJsonConfig(configPath, desktopConfig);
|
|
12930
13329
|
}
|
|
12931
13330
|
async cleanup(_scope, _manifest) {
|
|
12932
13331
|
removeRunworkMcpServers(getMcpConfigPath(), "mcpServers");
|
|
@@ -13037,7 +13436,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
13037
13436
|
const filePath = join28(projPath, file);
|
|
13038
13437
|
let stat;
|
|
13039
13438
|
try {
|
|
13040
|
-
stat =
|
|
13439
|
+
stat = statSync6(filePath);
|
|
13041
13440
|
} catch {
|
|
13042
13441
|
continue;
|
|
13043
13442
|
}
|
|
@@ -13062,10 +13461,13 @@ var init_claude_desktop = __esm(() => {
|
|
|
13062
13461
|
return null;
|
|
13063
13462
|
}
|
|
13064
13463
|
}
|
|
13464
|
+
transcriptSources() {
|
|
13465
|
+
return [{ path: getCoworkBaseDir(), kind: "dir" }];
|
|
13466
|
+
}
|
|
13065
13467
|
async listSessions(sinceISO) {
|
|
13066
13468
|
try {
|
|
13067
13469
|
const baseDir = getCoworkBaseDir();
|
|
13068
|
-
if (!
|
|
13470
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
13069
13471
|
return null;
|
|
13070
13472
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
13071
13473
|
let accounts;
|
|
@@ -13101,7 +13503,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
13101
13503
|
const filePath = join28(projPath, file);
|
|
13102
13504
|
let stat;
|
|
13103
13505
|
try {
|
|
13104
|
-
stat =
|
|
13506
|
+
stat = statSync6(filePath);
|
|
13105
13507
|
} catch {
|
|
13106
13508
|
continue;
|
|
13107
13509
|
}
|
|
@@ -13242,7 +13644,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
13242
13644
|
continue;
|
|
13243
13645
|
const orgPath = join28(baseDir, orgDir);
|
|
13244
13646
|
try {
|
|
13245
|
-
if (!
|
|
13647
|
+
if (!statSync6(orgPath).isDirectory())
|
|
13246
13648
|
continue;
|
|
13247
13649
|
} catch {
|
|
13248
13650
|
continue;
|
|
@@ -13252,7 +13654,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
13252
13654
|
continue;
|
|
13253
13655
|
const userPath = join28(orgPath, userDir);
|
|
13254
13656
|
try {
|
|
13255
|
-
if (!
|
|
13657
|
+
if (!statSync6(userPath).isDirectory())
|
|
13256
13658
|
continue;
|
|
13257
13659
|
} catch {
|
|
13258
13660
|
continue;
|
|
@@ -13380,6 +13782,7 @@ var init_cursor = __esm(async () => {
|
|
|
13380
13782
|
init_skill_removal();
|
|
13381
13783
|
init_json_config();
|
|
13382
13784
|
init_detection();
|
|
13785
|
+
init_transcript_sources();
|
|
13383
13786
|
await init_sqlite();
|
|
13384
13787
|
CursorAdapter = class CursorAdapter extends RegistryDetectedAdapter {
|
|
13385
13788
|
name = "Cursor";
|
|
@@ -13638,6 +14041,16 @@ ${instructions}`;
|
|
|
13638
14041
|
return null;
|
|
13639
14042
|
}
|
|
13640
14043
|
}
|
|
14044
|
+
transcriptSources() {
|
|
14045
|
+
return [
|
|
14046
|
+
{
|
|
14047
|
+
path: this.globalStorageDbPath(),
|
|
14048
|
+
kind: "file",
|
|
14049
|
+
tool: "sqlite",
|
|
14050
|
+
available: sqliteAvailable
|
|
14051
|
+
}
|
|
14052
|
+
];
|
|
14053
|
+
}
|
|
13641
14054
|
globalStorageDbPath() {
|
|
13642
14055
|
const os2 = platform5();
|
|
13643
14056
|
if (os2 === "darwin") {
|
|
@@ -13665,7 +14078,7 @@ ${instructions}`;
|
|
|
13665
14078
|
async listSessions(sinceISO) {
|
|
13666
14079
|
try {
|
|
13667
14080
|
const dbPath = this.globalStorageDbPath();
|
|
13668
|
-
if (!
|
|
14081
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
13669
14082
|
return null;
|
|
13670
14083
|
const rows = queryReadonlySqlite(dbPath, `SELECT json_object('id', json_extract(value,'$.composerId'), 'name', json_extract(value,'$.name'), 'createdAt', json_extract(value,'$.createdAt'), 'lastUpdatedAt', json_extract(value,'$.lastUpdatedAt')) FROM cursorDiskKV WHERE key LIKE 'composerData:%'`);
|
|
13671
14084
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
@@ -13794,7 +14207,7 @@ ${hint}`;
|
|
|
13794
14207
|
});
|
|
13795
14208
|
|
|
13796
14209
|
// src/agents/codex.ts
|
|
13797
|
-
import { existsSync as existsSync37, mkdirSync as mkdirSync21, readdirSync as readdirSync11, readFileSync as readFileSync27, statSync as
|
|
14210
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync21, readdirSync as readdirSync11, readFileSync as readFileSync27, statSync as statSync7, writeFileSync as writeFileSync21 } from "fs";
|
|
13798
14211
|
import { basename as basename3, join as join31 } from "path";
|
|
13799
14212
|
import { homedir as homedir13 } from "os";
|
|
13800
14213
|
import { parse, stringify } from "smol-toml";
|
|
@@ -13804,6 +14217,7 @@ function isRunworkManagedCodexKey(key) {
|
|
|
13804
14217
|
var CodexAdapter, CodexDesktopAdapter;
|
|
13805
14218
|
var init_codex = __esm(async () => {
|
|
13806
14219
|
init_types();
|
|
14220
|
+
init_transcript_sources();
|
|
13807
14221
|
init_session_digest();
|
|
13808
14222
|
init_session_listing();
|
|
13809
14223
|
init_skill_removal();
|
|
@@ -14022,6 +14436,12 @@ var init_codex = __esm(async () => {
|
|
|
14022
14436
|
return null;
|
|
14023
14437
|
}
|
|
14024
14438
|
}
|
|
14439
|
+
transcriptRoot() {
|
|
14440
|
+
return join31(homedir13(), ".codex", "sessions");
|
|
14441
|
+
}
|
|
14442
|
+
transcriptSources() {
|
|
14443
|
+
return [{ path: this.transcriptRoot(), kind: "dir" }];
|
|
14444
|
+
}
|
|
14025
14445
|
scanRolloutActivity(sinceMs) {
|
|
14026
14446
|
const days = new Set;
|
|
14027
14447
|
const result = {
|
|
@@ -14032,7 +14452,7 @@ var init_codex = __esm(async () => {
|
|
|
14032
14452
|
latestMs: 0,
|
|
14033
14453
|
activeDays: []
|
|
14034
14454
|
};
|
|
14035
|
-
const sessionsDir =
|
|
14455
|
+
const sessionsDir = this.transcriptRoot();
|
|
14036
14456
|
if (!existsSync37(sessionsDir))
|
|
14037
14457
|
return result;
|
|
14038
14458
|
const files = [];
|
|
@@ -14055,7 +14475,7 @@ var init_codex = __esm(async () => {
|
|
|
14055
14475
|
for (const file of files) {
|
|
14056
14476
|
let stat;
|
|
14057
14477
|
try {
|
|
14058
|
-
stat =
|
|
14478
|
+
stat = statSync7(file);
|
|
14059
14479
|
} catch {
|
|
14060
14480
|
continue;
|
|
14061
14481
|
}
|
|
@@ -14122,8 +14542,8 @@ var init_codex = __esm(async () => {
|
|
|
14122
14542
|
}
|
|
14123
14543
|
async readSessionDigests(sinceISO) {
|
|
14124
14544
|
try {
|
|
14125
|
-
const sessionsDir =
|
|
14126
|
-
if (!
|
|
14545
|
+
const sessionsDir = this.transcriptRoot();
|
|
14546
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
14127
14547
|
return null;
|
|
14128
14548
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
14129
14549
|
const files = [];
|
|
@@ -14147,7 +14567,7 @@ var init_codex = __esm(async () => {
|
|
|
14147
14567
|
for (const file of files) {
|
|
14148
14568
|
let stat;
|
|
14149
14569
|
try {
|
|
14150
|
-
stat =
|
|
14570
|
+
stat = statSync7(file);
|
|
14151
14571
|
} catch {
|
|
14152
14572
|
continue;
|
|
14153
14573
|
}
|
|
@@ -14170,8 +14590,8 @@ var init_codex = __esm(async () => {
|
|
|
14170
14590
|
}
|
|
14171
14591
|
async listSessions(sinceISO) {
|
|
14172
14592
|
try {
|
|
14173
|
-
const sessionsDir =
|
|
14174
|
-
if (!
|
|
14593
|
+
const sessionsDir = this.transcriptRoot();
|
|
14594
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
14175
14595
|
return null;
|
|
14176
14596
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
14177
14597
|
const files = [];
|
|
@@ -14195,7 +14615,7 @@ var init_codex = __esm(async () => {
|
|
|
14195
14615
|
for (const file of files) {
|
|
14196
14616
|
let stat;
|
|
14197
14617
|
try {
|
|
14198
|
-
stat =
|
|
14618
|
+
stat = statSync7(file);
|
|
14199
14619
|
} catch {
|
|
14200
14620
|
continue;
|
|
14201
14621
|
}
|
|
@@ -14234,7 +14654,7 @@ var init_codex = __esm(async () => {
|
|
|
14234
14654
|
}
|
|
14235
14655
|
async readSkillUsage(lastSyncAt) {
|
|
14236
14656
|
try {
|
|
14237
|
-
const sessionsDir =
|
|
14657
|
+
const sessionsDir = this.transcriptRoot();
|
|
14238
14658
|
if (!existsSync37(sessionsDir))
|
|
14239
14659
|
return null;
|
|
14240
14660
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -14251,7 +14671,7 @@ var init_codex = __esm(async () => {
|
|
|
14251
14671
|
if (entry.endsWith(".jsonl")) {
|
|
14252
14672
|
let fileStat;
|
|
14253
14673
|
try {
|
|
14254
|
-
fileStat =
|
|
14674
|
+
fileStat = statSync7(fullPath);
|
|
14255
14675
|
} catch {
|
|
14256
14676
|
continue;
|
|
14257
14677
|
}
|
|
@@ -14260,7 +14680,7 @@ var init_codex = __esm(async () => {
|
|
|
14260
14680
|
this.parseRolloutForSkills(fullPath, sinceMs, skillCounts);
|
|
14261
14681
|
} else {
|
|
14262
14682
|
try {
|
|
14263
|
-
if (
|
|
14683
|
+
if (statSync7(fullPath).isDirectory())
|
|
14264
14684
|
walkDir2(fullPath);
|
|
14265
14685
|
} catch {
|
|
14266
14686
|
continue;
|
|
@@ -14381,6 +14801,12 @@ var init_codex = __esm(async () => {
|
|
|
14381
14801
|
async listSessions() {
|
|
14382
14802
|
return null;
|
|
14383
14803
|
}
|
|
14804
|
+
async readSessionDigests() {
|
|
14805
|
+
return null;
|
|
14806
|
+
}
|
|
14807
|
+
transcriptSources() {
|
|
14808
|
+
return [];
|
|
14809
|
+
}
|
|
14384
14810
|
};
|
|
14385
14811
|
});
|
|
14386
14812
|
|
|
@@ -14468,7 +14894,7 @@ var init_cline = __esm(() => {
|
|
|
14468
14894
|
}
|
|
14469
14895
|
}
|
|
14470
14896
|
mkdirSync22(join32(globalStatePath, ".."), { recursive: true });
|
|
14471
|
-
|
|
14897
|
+
writeJsonConfig(globalStatePath, state);
|
|
14472
14898
|
}
|
|
14473
14899
|
async removeSkills(skillFilenames, scope) {
|
|
14474
14900
|
if (scope !== "project")
|
|
@@ -14503,7 +14929,7 @@ var init_cline = __esm(() => {
|
|
|
14503
14929
|
});
|
|
14504
14930
|
|
|
14505
14931
|
// src/agents/gemini.ts
|
|
14506
|
-
import { existsSync as existsSync39, mkdirSync as mkdirSync23, readdirSync as readdirSync13, readFileSync as readFileSync29, statSync as
|
|
14932
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync23, readdirSync as readdirSync13, readFileSync as readFileSync29, statSync as statSync8, writeFileSync as writeFileSync23 } from "fs";
|
|
14507
14933
|
import { basename as basename4, join as join33 } from "path";
|
|
14508
14934
|
import { homedir as homedir15 } from "os";
|
|
14509
14935
|
var GeminiAdapter;
|
|
@@ -14513,6 +14939,7 @@ var init_gemini = __esm(() => {
|
|
|
14513
14939
|
init_skill_removal();
|
|
14514
14940
|
init_trash();
|
|
14515
14941
|
init_types();
|
|
14942
|
+
init_transcript_sources();
|
|
14516
14943
|
init_instruction_hint();
|
|
14517
14944
|
init_json_config();
|
|
14518
14945
|
init_detection();
|
|
@@ -14581,11 +15008,11 @@ var init_gemini = __esm(() => {
|
|
|
14581
15008
|
settings.tools.exclude = config.permissionRules.deny;
|
|
14582
15009
|
}
|
|
14583
15010
|
mkdirSync23(join33(settingsPath, ".."), { recursive: true });
|
|
14584
|
-
|
|
15011
|
+
writeJsonConfig(settingsPath, settings);
|
|
14585
15012
|
}
|
|
14586
15013
|
async readUsageStats(lastSyncAt) {
|
|
14587
15014
|
try {
|
|
14588
|
-
const tmpDir =
|
|
15015
|
+
const tmpDir = this.transcriptRoot();
|
|
14589
15016
|
if (!existsSync39(tmpDir))
|
|
14590
15017
|
return null;
|
|
14591
15018
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -14615,7 +15042,7 @@ var init_gemini = __esm(() => {
|
|
|
14615
15042
|
const filePath = join33(chatsDir, file.name);
|
|
14616
15043
|
let stat;
|
|
14617
15044
|
try {
|
|
14618
|
-
stat =
|
|
15045
|
+
stat = statSync8(filePath);
|
|
14619
15046
|
} catch {
|
|
14620
15047
|
continue;
|
|
14621
15048
|
}
|
|
@@ -14663,8 +15090,14 @@ var init_gemini = __esm(() => {
|
|
|
14663
15090
|
return null;
|
|
14664
15091
|
}
|
|
14665
15092
|
}
|
|
15093
|
+
transcriptRoot() {
|
|
15094
|
+
return join33(homedir15(), ".gemini", "tmp");
|
|
15095
|
+
}
|
|
15096
|
+
transcriptSources() {
|
|
15097
|
+
return [{ path: this.transcriptRoot(), kind: "dir" }];
|
|
15098
|
+
}
|
|
14666
15099
|
*chatFiles(sinceMs) {
|
|
14667
|
-
const tmpDir =
|
|
15100
|
+
const tmpDir = this.transcriptRoot();
|
|
14668
15101
|
if (!existsSync39(tmpDir))
|
|
14669
15102
|
return;
|
|
14670
15103
|
let projects;
|
|
@@ -14689,7 +15122,7 @@ var init_gemini = __esm(() => {
|
|
|
14689
15122
|
const filePath = join33(chatsDir, file);
|
|
14690
15123
|
let stat;
|
|
14691
15124
|
try {
|
|
14692
|
-
stat =
|
|
15125
|
+
stat = statSync8(filePath);
|
|
14693
15126
|
} catch {
|
|
14694
15127
|
continue;
|
|
14695
15128
|
}
|
|
@@ -14701,6 +15134,8 @@ var init_gemini = __esm(() => {
|
|
|
14701
15134
|
}
|
|
14702
15135
|
async listSessions(sinceISO) {
|
|
14703
15136
|
try {
|
|
15137
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
15138
|
+
return null;
|
|
14704
15139
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
14705
15140
|
const sessions = [];
|
|
14706
15141
|
for (const { filePath, project, mtimeMs } of this.chatFiles(sinceMs)) {
|
|
@@ -14740,6 +15175,8 @@ var init_gemini = __esm(() => {
|
|
|
14740
15175
|
}
|
|
14741
15176
|
async readSessionDigests(sinceISO) {
|
|
14742
15177
|
try {
|
|
15178
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
15179
|
+
return null;
|
|
14743
15180
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
14744
15181
|
const digests = [];
|
|
14745
15182
|
for (const { filePath, project } of this.chatFiles(sinceMs)) {
|
|
@@ -14775,7 +15212,7 @@ var init_gemini = __esm(() => {
|
|
|
14775
15212
|
});
|
|
14776
15213
|
|
|
14777
15214
|
// src/agents/generic-adapter.ts
|
|
14778
|
-
import {
|
|
15215
|
+
import { mkdirSync as mkdirSync24, writeFileSync as writeFileSync24 } from "fs";
|
|
14779
15216
|
import { join as join34 } from "path";
|
|
14780
15217
|
import { homedir as homedir16 } from "os";
|
|
14781
15218
|
var GenericAgentAdapter;
|
|
@@ -14785,7 +15222,9 @@ var init_generic_adapter = __esm(() => {
|
|
|
14785
15222
|
init_json_config();
|
|
14786
15223
|
init_instruction_hint();
|
|
14787
15224
|
init_registry();
|
|
15225
|
+
init_detection_probes();
|
|
14788
15226
|
init_detection();
|
|
15227
|
+
init_trash();
|
|
14789
15228
|
GenericAgentAdapter = class GenericAgentAdapter extends RegistryDetectedAdapter {
|
|
14790
15229
|
name;
|
|
14791
15230
|
slug;
|
|
@@ -14808,7 +15247,13 @@ var init_generic_adapter = __esm(() => {
|
|
|
14808
15247
|
async writeMcpServers(servers, _scope) {
|
|
14809
15248
|
if (!this.def.mcpConfigPath)
|
|
14810
15249
|
return;
|
|
14811
|
-
const
|
|
15250
|
+
const resolved = resolvePlatformString(this.def.mcpConfigPath) || "";
|
|
15251
|
+
const expanded = expandWindowsPathTemplate(resolved, {
|
|
15252
|
+
"%APPDATA%": process.env.APPDATA,
|
|
15253
|
+
"%LOCALAPPDATA%": process.env.LOCALAPPDATA,
|
|
15254
|
+
"%ProgramFiles%": process.env.ProgramFiles
|
|
15255
|
+
});
|
|
15256
|
+
const filePath = expanded.needsHomeJoin ? join34(homedir16(), expanded.path) : expanded.path;
|
|
14812
15257
|
if (!filePath)
|
|
14813
15258
|
return;
|
|
14814
15259
|
const entries = {};
|
|
@@ -14832,12 +15277,7 @@ var init_generic_adapter = __esm(() => {
|
|
|
14832
15277
|
return 0;
|
|
14833
15278
|
for (const skill of skills) {
|
|
14834
15279
|
if (skill.name !== skill.filename) {
|
|
14835
|
-
|
|
14836
|
-
if (existsSync40(oldDir)) {
|
|
14837
|
-
try {
|
|
14838
|
-
rmSync12(oldDir, { recursive: true, force: true });
|
|
14839
|
-
} catch {}
|
|
14840
|
-
}
|
|
15280
|
+
moveToTrash(join34(baseDir, skill.name), `skill renamed to ${skill.filename}`);
|
|
14841
15281
|
}
|
|
14842
15282
|
const skillDir = join34(baseDir, skill.filename);
|
|
14843
15283
|
mkdirSync24(skillDir, { recursive: true });
|
|
@@ -14966,6 +15406,26 @@ var init_detect = __esm(async () => {
|
|
|
14966
15406
|
ALL_ADAPTERS = buildAllAdapters();
|
|
14967
15407
|
});
|
|
14968
15408
|
|
|
15409
|
+
// ../../shared/types/local-conversations.ts
|
|
15410
|
+
function describeScanOutcome(outcome) {
|
|
15411
|
+
switch (outcome.status) {
|
|
15412
|
+
case "ok":
|
|
15413
|
+
case "no-reader":
|
|
15414
|
+
return null;
|
|
15415
|
+
case "missing-dir":
|
|
15416
|
+
return `no conversation history found${outcome.path ? ` at ${outcome.path}` : ""}`;
|
|
15417
|
+
case "permission-denied":
|
|
15418
|
+
return `access denied${outcome.path ? ` to ${outcome.path}` : ""}`;
|
|
15419
|
+
case "missing-tool":
|
|
15420
|
+
return `needs ${outcome.detail ?? "a tool that is not installed"}`;
|
|
15421
|
+
case "error":
|
|
15422
|
+
return `could not be read${outcome.detail ? `: ${outcome.detail}` : ""}`;
|
|
15423
|
+
}
|
|
15424
|
+
}
|
|
15425
|
+
function scanOutcomeNeedsAttention(outcome) {
|
|
15426
|
+
return outcome.status === "permission-denied" || outcome.status === "error";
|
|
15427
|
+
}
|
|
15428
|
+
|
|
14969
15429
|
// src/utils/insight-id.ts
|
|
14970
15430
|
import { createHash as createHash3 } from "node:crypto";
|
|
14971
15431
|
function computeInsightId(userSeed, localKey) {
|
|
@@ -15116,7 +15576,7 @@ __export(exports_run_log, {
|
|
|
15116
15576
|
RUN_LOG_CAP: () => RUN_LOG_CAP,
|
|
15117
15577
|
ANALYST_ERROR_MAX_CHARS: () => ANALYST_ERROR_MAX_CHARS
|
|
15118
15578
|
});
|
|
15119
|
-
import { existsSync as existsSync43, mkdirSync as mkdirSync25, readFileSync as readFileSync32, readdirSync as readdirSync15, statSync as
|
|
15579
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync25, readFileSync as readFileSync32, readdirSync as readdirSync15, statSync as statSync9, unlinkSync as unlinkSync7, writeFileSync as writeFileSync25 } from "fs";
|
|
15120
15580
|
import { dirname as dirname10, join as join37 } from "path";
|
|
15121
15581
|
import { homedir as homedir19 } from "os";
|
|
15122
15582
|
function runLogPath() {
|
|
@@ -15260,7 +15720,7 @@ function acquireRunLock(nowMs = Date.now()) {
|
|
|
15260
15720
|
return true;
|
|
15261
15721
|
} catch {
|
|
15262
15722
|
try {
|
|
15263
|
-
if (nowMs -
|
|
15723
|
+
if (nowMs - statSync9(p).mtimeMs < RUN_STALE_MS)
|
|
15264
15724
|
return false;
|
|
15265
15725
|
unlinkSync7(p);
|
|
15266
15726
|
} catch {
|
|
@@ -15594,10 +16054,12 @@ function selectAnalyst(requestedSlug) {
|
|
|
15594
16054
|
const resolved = resolve3(requested);
|
|
15595
16055
|
if (!resolved)
|
|
15596
16056
|
return { candidates: [], broken };
|
|
15597
|
-
|
|
16057
|
+
const probe = probeBinaryRunnable(resolved.command);
|
|
16058
|
+
if (probe.runnable) {
|
|
15598
16059
|
return { chosen: resolved, candidates: [resolved], broken };
|
|
15599
16060
|
}
|
|
15600
|
-
|
|
16061
|
+
if (probe.reason !== "timeout")
|
|
16062
|
+
broken.push(requested.binary);
|
|
15601
16063
|
return { candidates: [], broken };
|
|
15602
16064
|
}
|
|
15603
16065
|
const candidates = [];
|
|
@@ -15605,9 +16067,10 @@ function selectAnalyst(requestedSlug) {
|
|
|
15605
16067
|
const resolved = resolve3(a);
|
|
15606
16068
|
if (!resolved)
|
|
15607
16069
|
continue;
|
|
15608
|
-
|
|
16070
|
+
const probe = probeBinaryRunnable(resolved.command);
|
|
16071
|
+
if (probe.runnable)
|
|
15609
16072
|
candidates.push(resolved);
|
|
15610
|
-
else
|
|
16073
|
+
else if (probe.reason !== "timeout")
|
|
15611
16074
|
broken.push(a.binary);
|
|
15612
16075
|
}
|
|
15613
16076
|
return { chosen: candidates[0], candidates, broken };
|
|
@@ -15945,6 +16408,7 @@ var init_reflect = __esm(async () => {
|
|
|
15945
16408
|
init_store();
|
|
15946
16409
|
init_client();
|
|
15947
16410
|
init_resolve();
|
|
16411
|
+
init_transcript_sources();
|
|
15948
16412
|
init_session_digest();
|
|
15949
16413
|
init_insight_id();
|
|
15950
16414
|
init_insight_store();
|
|
@@ -16009,13 +16473,13 @@ var init_reflect = __esm(async () => {
|
|
|
16009
16473
|
const seen = new Set;
|
|
16010
16474
|
for (const adapter2 of adapters) {
|
|
16011
16475
|
if (!adapter2.readSessionDigests) {
|
|
16012
|
-
perAgent.push({
|
|
16476
|
+
perAgent.push({ agentSlug: adapter2.slug, status: "no-reader", sessions: null });
|
|
16013
16477
|
continue;
|
|
16014
16478
|
}
|
|
16015
16479
|
try {
|
|
16016
16480
|
const sessions = await adapter2.readSessionDigests(sinceISO);
|
|
16017
16481
|
if (sessions === null) {
|
|
16018
|
-
perAgent.push(
|
|
16482
|
+
perAgent.push(diagnoseTranscriptRead(adapter2));
|
|
16019
16483
|
continue;
|
|
16020
16484
|
}
|
|
16021
16485
|
let added = 0;
|
|
@@ -16027,20 +16491,26 @@ var init_reflect = __esm(async () => {
|
|
|
16027
16491
|
digests.push(s);
|
|
16028
16492
|
added++;
|
|
16029
16493
|
}
|
|
16030
|
-
perAgent.push({
|
|
16031
|
-
} catch {
|
|
16032
|
-
perAgent.push(
|
|
16494
|
+
perAgent.push({ agentSlug: adapter2.slug, status: "ok", sessions: added });
|
|
16495
|
+
} catch (err) {
|
|
16496
|
+
perAgent.push(diagnoseTranscriptRead(adapter2, err));
|
|
16033
16497
|
}
|
|
16034
16498
|
}
|
|
16035
16499
|
if (!json) {
|
|
16036
16500
|
console.error(bold(`
|
|
16037
16501
|
Reflection over the last ${days} days`));
|
|
16038
|
-
for (const a of perAgent)
|
|
16039
|
-
|
|
16502
|
+
for (const a of perAgent) {
|
|
16503
|
+
const reason = describeScanOutcome(a);
|
|
16504
|
+
if (reason === null) {
|
|
16505
|
+
console.error(` ${cyan(a.agentSlug)}: ${a.status === "no-reader" ? gray("no transcript reader") : `${a.sessions} session(s)`}`);
|
|
16506
|
+
} else {
|
|
16507
|
+
console.error(` ${cyan(a.agentSlug)}: ${scanOutcomeNeedsAttention(a) ? red(reason) : gray(reason)}`);
|
|
16508
|
+
}
|
|
16509
|
+
}
|
|
16040
16510
|
}
|
|
16041
16511
|
if (digests.length === 0) {
|
|
16042
16512
|
if (json) {
|
|
16043
|
-
jsonOut({ insights: [], reason: "no-sessions" });
|
|
16513
|
+
jsonOut({ insights: [], reason: "no-sessions", perAgent });
|
|
16044
16514
|
return;
|
|
16045
16515
|
}
|
|
16046
16516
|
console.error(yellow(`
|
|
@@ -16405,19 +16875,19 @@ async function listLocalConversations(opts = {}) {
|
|
|
16405
16875
|
const perAgent = [];
|
|
16406
16876
|
for (const adapter2 of adapters) {
|
|
16407
16877
|
if (!adapter2.listSessions) {
|
|
16408
|
-
perAgent.push({
|
|
16878
|
+
perAgent.push({ agentSlug: adapter2.slug, status: "no-reader", sessions: null });
|
|
16409
16879
|
continue;
|
|
16410
16880
|
}
|
|
16411
16881
|
try {
|
|
16412
16882
|
const sessions = await adapter2.listSessions(sinceISO);
|
|
16413
16883
|
if (sessions === null) {
|
|
16414
|
-
perAgent.push(
|
|
16884
|
+
perAgent.push(diagnoseTranscriptRead(adapter2));
|
|
16415
16885
|
continue;
|
|
16416
16886
|
}
|
|
16417
16887
|
listings.push(sessions);
|
|
16418
|
-
perAgent.push({
|
|
16419
|
-
} catch {
|
|
16420
|
-
perAgent.push(
|
|
16888
|
+
perAgent.push({ agentSlug: adapter2.slug, status: "ok", sessions: sessions.length });
|
|
16889
|
+
} catch (err) {
|
|
16890
|
+
perAgent.push(diagnoseTranscriptRead(adapter2, err));
|
|
16421
16891
|
}
|
|
16422
16892
|
}
|
|
16423
16893
|
const conversations = mergeSessionListings(listings, {
|
|
@@ -16428,6 +16898,7 @@ async function listLocalConversations(opts = {}) {
|
|
|
16428
16898
|
}
|
|
16429
16899
|
var DEFAULT_IDLE_MINUTES = 10;
|
|
16430
16900
|
var init_conversation_registry = __esm(async () => {
|
|
16901
|
+
init_transcript_sources();
|
|
16431
16902
|
await init_detect();
|
|
16432
16903
|
});
|
|
16433
16904
|
|
|
@@ -17824,7 +18295,7 @@ var init_welcome = __esm(() => {
|
|
|
17824
18295
|
});
|
|
17825
18296
|
|
|
17826
18297
|
// src/index.ts
|
|
17827
|
-
import { Command as
|
|
18298
|
+
import { Command as Command40 } from "commander";
|
|
17828
18299
|
|
|
17829
18300
|
// src/commands/login.ts
|
|
17830
18301
|
init_login_flow();
|
|
@@ -18133,7 +18604,18 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
18133
18604
|
console.log("Syncing...");
|
|
18134
18605
|
try {
|
|
18135
18606
|
commitWorkingTree(cwd, `deploy: ${new Date().toISOString().replace("T", " ").slice(0, 19)}`);
|
|
18136
|
-
} catch {
|
|
18607
|
+
} catch (err) {
|
|
18608
|
+
if (hasTrackedChanges(cwd)) {
|
|
18609
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
18610
|
+
if (useJson) {
|
|
18611
|
+
jsonOut(buildErrorResponse("deploy", "Could not commit your changes", `Your working tree has uncommitted changes that could not be committed (${detail.slice(0, 300)}). Deploying now would ship the PREVIOUS commit while reporting success.`, ["Check for a failing pre-commit hook or a stale .git/index.lock", "Commit the changes yourself with git commit", "Then re-run runwork deploy"]));
|
|
18612
|
+
process.exit(1);
|
|
18613
|
+
}
|
|
18614
|
+
console.error("Could not commit your changes, so nothing new would be deployed.");
|
|
18615
|
+
console.error("A pre-commit hook or a stale .git/index.lock may be interfering. Commit manually, then re-run `runwork deploy`.");
|
|
18616
|
+
process.exit(1);
|
|
18617
|
+
}
|
|
18618
|
+
}
|
|
18137
18619
|
if (!hasCommits(cwd)) {
|
|
18138
18620
|
if (useJson) {
|
|
18139
18621
|
jsonOut(buildErrorResponse("deploy", "Nothing to deploy", "No commits exist and the working tree has no changes to commit, so there is nothing to sync or deploy.", ["Make changes to your app first", "Run runwork dev to develop and verify changes", "Then run runwork deploy"]));
|
|
@@ -20274,9 +20756,14 @@ conversationsCommand.command("list").description("List local conversations acros
|
|
|
20274
20756
|
Local conversations, last ${days} day(s)` : `
|
|
20275
20757
|
Local conversations (all)`));
|
|
20276
20758
|
for (const a of result.perAgent) {
|
|
20277
|
-
if (a.
|
|
20759
|
+
if (a.status === "no-reader")
|
|
20278
20760
|
continue;
|
|
20279
|
-
|
|
20761
|
+
const reason = describeScanOutcome(a);
|
|
20762
|
+
if (reason === null) {
|
|
20763
|
+
console.log(dim(` ${a.agentSlug}: ${a.sessions} session(s)`));
|
|
20764
|
+
} else {
|
|
20765
|
+
console.log(` ${a.agentSlug}: ${scanOutcomeNeedsAttention(a) ? yellow(reason) : dim(reason)}`);
|
|
20766
|
+
}
|
|
20280
20767
|
}
|
|
20281
20768
|
console.log("");
|
|
20282
20769
|
const width = process.stdout.columns || 120;
|
|
@@ -20293,6 +20780,26 @@ Local conversations (all)`));
|
|
|
20293
20780
|
if (result.conversations.length === 0)
|
|
20294
20781
|
console.log(dim(" none"));
|
|
20295
20782
|
});
|
|
20783
|
+
conversationsCommand.command("status").description("Why each agent did or did not contribute conversations (no conversation data)").option("--days <n>", "Look back this many days (0 = no limit)", String(DEFAULT_LOOKBACK_DAYS)).action(async (opts, command) => {
|
|
20784
|
+
const json = command.optsWithGlobals().json === true || !process.stdout.isTTY;
|
|
20785
|
+
const days = parseDays(opts.days);
|
|
20786
|
+
const { perAgent } = await listLocalConversations({ sinceISO: sinceFromDays(days) });
|
|
20787
|
+
if (json) {
|
|
20788
|
+
jsonOut({ perAgent });
|
|
20789
|
+
return;
|
|
20790
|
+
}
|
|
20791
|
+
console.log(bold(days > 0 ? `
|
|
20792
|
+
Conversation sources, last ${days} day(s)` : `
|
|
20793
|
+
Conversation sources`));
|
|
20794
|
+
for (const a of perAgent) {
|
|
20795
|
+
const reason = describeScanOutcome(a);
|
|
20796
|
+
if (reason === null) {
|
|
20797
|
+
console.log(` ${cyan(a.agentSlug)}: ${a.status === "no-reader" ? dim("no transcript reader") : `${a.sessions} conversation(s)`}`);
|
|
20798
|
+
} else {
|
|
20799
|
+
console.log(` ${cyan(a.agentSlug)}: ${scanOutcomeNeedsAttention(a) ? yellow(reason) : dim(reason)}`);
|
|
20800
|
+
}
|
|
20801
|
+
}
|
|
20802
|
+
});
|
|
20296
20803
|
conversationsCommand.command("scan").description("Detect finished conversations (idle threshold) and queue them for reflection").option("--days <n>", "Look back this many days (0 = no limit)", String(DEFAULT_LOOKBACK_DAYS)).option("--idle-minutes <n>", "Idle threshold that marks a conversation finished", String(DEFAULT_IDLE_MINUTES)).action(async (opts, command) => {
|
|
20297
20804
|
const json = command.optsWithGlobals().json === true || !process.stdout.isTTY;
|
|
20298
20805
|
const days = parseDays(opts.days);
|
|
@@ -20356,11 +20863,12 @@ conversationsCommand.command("scan").description("Detect finished conversations
|
|
|
20356
20863
|
const snapshot = {
|
|
20357
20864
|
generatedAt: new Date().toISOString(),
|
|
20358
20865
|
idleMinutes,
|
|
20359
|
-
conversations: [...listed, ...rescued].sort((a, b) => b.lastActivityAt.localeCompare(a.lastActivityAt))
|
|
20866
|
+
conversations: [...listed, ...rescued].sort((a, b) => b.lastActivityAt.localeCompare(a.lastActivityAt)),
|
|
20867
|
+
perAgent: result.perAgent
|
|
20360
20868
|
};
|
|
20361
20869
|
writeJsonAtomic(join43(homedir24(), ".runwork", "conversations.json"), snapshot);
|
|
20362
20870
|
if (json) {
|
|
20363
|
-
jsonOut({ queued: scan.queued, reopened: scan.reopened, pruned: scan.pruned, pending, finished: finished.length });
|
|
20871
|
+
jsonOut({ queued: scan.queued, reopened: scan.reopened, pruned: scan.pruned, pending, finished: finished.length, perAgent: result.perAgent });
|
|
20364
20872
|
return;
|
|
20365
20873
|
}
|
|
20366
20874
|
console.log(green(`Scanned ${result.conversations.length} conversation(s): ${scan.queued} newly queued, ${scan.reopened} reopened, ${pending} pending analysis.`));
|
|
@@ -23335,7 +23843,11 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
23335
23843
|
if (!action.remoteContent)
|
|
23336
23844
|
continue;
|
|
23337
23845
|
const skillFile = makeSkillFile(action.name, action.remoteContent);
|
|
23338
|
-
await writeSkillToAgents(skillFile, action.source, ctx);
|
|
23846
|
+
const outcome = await writeSkillToAgents(skillFile, action.source, ctx);
|
|
23847
|
+
if (!writeLanded(outcome)) {
|
|
23848
|
+
vlog(` Pull FAILED for ${action.name} (${outcome.succeeded}/${outcome.attempted} agent writes); will retry next sync`);
|
|
23849
|
+
continue;
|
|
23850
|
+
}
|
|
23339
23851
|
newHashes[action.name] = {
|
|
23340
23852
|
localHash: contentHash(buildSkillMd2(skillFile)),
|
|
23341
23853
|
remoteHash: contentHash(action.remoteContent),
|
|
@@ -23377,7 +23889,11 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
23377
23889
|
vlog(` Pushed (conflict resolved): ${action.name}`);
|
|
23378
23890
|
} else if (resolution === "remote" && action.remoteContent) {
|
|
23379
23891
|
const skillFile = makeSkillFile(action.name, action.remoteContent);
|
|
23380
|
-
await writeSkillToAgents(skillFile, action.source, ctx);
|
|
23892
|
+
const outcome = await writeSkillToAgents(skillFile, action.source, ctx);
|
|
23893
|
+
if (!writeLanded(outcome)) {
|
|
23894
|
+
vlog(` Conflict pull FAILED for ${action.name} (${outcome.succeeded}/${outcome.attempted} agent writes); will retry next sync`);
|
|
23895
|
+
continue;
|
|
23896
|
+
}
|
|
23381
23897
|
newHashes[action.name] = {
|
|
23382
23898
|
localHash: contentHash(buildSkillMd2(skillFile)),
|
|
23383
23899
|
remoteHash: contentHash(action.remoteContent),
|
|
@@ -23389,6 +23905,7 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
23389
23905
|
}
|
|
23390
23906
|
for (const _action of plan.skips) {}
|
|
23391
23907
|
const deletionSlugs = plan.deletions.map((action) => toSlug(action.name));
|
|
23908
|
+
let removalsFailed = false;
|
|
23392
23909
|
if (deletionSlugs.length) {
|
|
23393
23910
|
for (const adapter2 of ctx.adapters) {
|
|
23394
23911
|
if (!adapter2.supportsSkills() || !adapter2.removeSkills)
|
|
@@ -23396,15 +23913,20 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
23396
23913
|
for (const scope of ctx.scopes) {
|
|
23397
23914
|
try {
|
|
23398
23915
|
await adapter2.removeSkills(deletionSlugs, scope);
|
|
23399
|
-
} catch {
|
|
23916
|
+
} catch {
|
|
23917
|
+
removalsFailed = true;
|
|
23918
|
+
}
|
|
23400
23919
|
}
|
|
23401
23920
|
}
|
|
23402
23921
|
for (const action of plan.deletions) {
|
|
23403
|
-
vlog(` Deleted locally: ${action.name}`);
|
|
23922
|
+
vlog(removalsFailed ? ` Deletion of ${action.name} incomplete; will retry next sync` : ` Deleted locally: ${action.name}`);
|
|
23404
23923
|
}
|
|
23405
23924
|
}
|
|
23406
|
-
return
|
|
23407
|
-
|
|
23925
|
+
return {
|
|
23926
|
+
newHashes,
|
|
23927
|
+
failedDeletions: removalsFailed ? plan.deletions.map((action) => action.name) : []
|
|
23928
|
+
};
|
|
23929
|
+
}
|
|
23408
23930
|
function makeSkillFile(name, content) {
|
|
23409
23931
|
return {
|
|
23410
23932
|
name,
|
|
@@ -23414,17 +23936,25 @@ function makeSkillFile(name, content) {
|
|
|
23414
23936
|
};
|
|
23415
23937
|
}
|
|
23416
23938
|
async function writeSkillToAgents(skillFile, source, ctx) {
|
|
23939
|
+
let attempted = 0;
|
|
23940
|
+
let succeeded = 0;
|
|
23417
23941
|
for (const adapter2 of ctx.adapters) {
|
|
23418
23942
|
if (source === "app" && ctx.hasMcp && adapter2.mcpProvidesSkills)
|
|
23419
23943
|
continue;
|
|
23420
23944
|
for (const scope of ctx.scopes) {
|
|
23421
23945
|
if (!adapter2.supportsSkills())
|
|
23422
23946
|
continue;
|
|
23947
|
+
attempted++;
|
|
23423
23948
|
try {
|
|
23424
23949
|
await adapter2.writeSkills([skillFile], scope);
|
|
23950
|
+
succeeded++;
|
|
23425
23951
|
} catch {}
|
|
23426
23952
|
}
|
|
23427
23953
|
}
|
|
23954
|
+
return { attempted, succeeded };
|
|
23955
|
+
}
|
|
23956
|
+
function writeLanded(outcome) {
|
|
23957
|
+
return outcome.attempted === 0 || outcome.succeeded > 0;
|
|
23428
23958
|
}
|
|
23429
23959
|
function extractDescription(content) {
|
|
23430
23960
|
const match = content.match(/^---\n[\s\S]*?description:\s*(.+)\n[\s\S]*?---/);
|
|
@@ -23839,7 +24369,7 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23839
24369
|
if (adapters.length > 0) {
|
|
23840
24370
|
console.log(` Syncing to: ${adapters.map((a) => a.name).join(", ")}`);
|
|
23841
24371
|
}
|
|
23842
|
-
const newHashes = await executeSyncPlan(plan, resolvedConflicts, {
|
|
24372
|
+
const { newHashes, failedDeletions } = await executeSyncPlan(plan, resolvedConflicts, {
|
|
23843
24373
|
client,
|
|
23844
24374
|
workspaceId: state.workspaceId,
|
|
23845
24375
|
adapters,
|
|
@@ -23896,9 +24426,15 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23896
24426
|
};
|
|
23897
24427
|
const mcpFailedAdapters = new Set;
|
|
23898
24428
|
const skillFailedAdapters = new Set;
|
|
24429
|
+
const instructionFailedAdapters = new Set;
|
|
24430
|
+
const configFailedAdapters = new Set;
|
|
24431
|
+
const hookFailedAdapters = new Set;
|
|
24432
|
+
const agentOutcomes = {};
|
|
24433
|
+
const syncStartedAt = new Date().toISOString();
|
|
23899
24434
|
for (const adapter2 of adapters) {
|
|
23900
24435
|
if (isConnectOnlyAgent(getRegistryAgent(adapter2.slug))) {
|
|
23901
24436
|
vlog(` [${adapter2.name}] Connect-only agent: no local files to sync`);
|
|
24437
|
+
agentOutcomes[adapter2.slug] = { at: syncStartedAt, ok: true, skipped: true };
|
|
23902
24438
|
summary.adaptersProcessed++;
|
|
23903
24439
|
continue;
|
|
23904
24440
|
}
|
|
@@ -23960,15 +24496,32 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23960
24496
|
await adapter2.writeInstructionHint(instructionHint, scope);
|
|
23961
24497
|
summary.instructionHintWrites++;
|
|
23962
24498
|
vlog(` [${adapter2.name}] Updated instruction hints (${scope})`);
|
|
23963
|
-
|
|
24499
|
+
} catch (err) {
|
|
24500
|
+
adapterFailedAnyScope = true;
|
|
24501
|
+
instructionFailedAdapters.add(adapter2.slug);
|
|
24502
|
+
console.warn(` [${adapter2.name}] Failed instructions (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
24503
|
+
}
|
|
24504
|
+
if (adapter2.writeBuiltInHooks) {
|
|
24505
|
+
try {
|
|
23964
24506
|
await adapter2.writeBuiltInHooks(scope);
|
|
23965
24507
|
summary.hookInstallCalls++;
|
|
24508
|
+
} catch (err) {
|
|
24509
|
+
adapterFailedAnyScope = true;
|
|
24510
|
+
hookFailedAdapters.add(adapter2.slug);
|
|
24511
|
+
console.warn(` [${adapter2.name}] Failed hooks (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
23966
24512
|
}
|
|
23967
|
-
} catch (err) {
|
|
23968
|
-
adapterFailedAnyScope = true;
|
|
23969
|
-
console.warn(` [${adapter2.name}] Failed (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
23970
24513
|
}
|
|
23971
24514
|
}
|
|
24515
|
+
const failedDimensions = [];
|
|
24516
|
+
if (skillFailedAdapters.has(adapter2.slug))
|
|
24517
|
+
failedDimensions.push("skills");
|
|
24518
|
+
if (mcpFailedAdapters.has(adapter2.slug))
|
|
24519
|
+
failedDimensions.push("mcp");
|
|
24520
|
+
if (instructionFailedAdapters.has(adapter2.slug))
|
|
24521
|
+
failedDimensions.push("instructions");
|
|
24522
|
+
if (hookFailedAdapters.has(adapter2.slug))
|
|
24523
|
+
failedDimensions.push("hooks");
|
|
24524
|
+
agentOutcomes[adapter2.slug] = failedDimensions.length > 0 ? { at: syncStartedAt, ok: false, failed: failedDimensions } : { at: syncStartedAt, ok: true };
|
|
23972
24525
|
summary.adaptersProcessed++;
|
|
23973
24526
|
if (adapterFailedAnyScope)
|
|
23974
24527
|
summary.adaptersFailed++;
|
|
@@ -23999,7 +24552,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23999
24552
|
await adapter2.writeTeamInstructions(fullInstructions, scope);
|
|
24000
24553
|
teamInstructionsApplied = true;
|
|
24001
24554
|
vlog(` [${adapter2.name}] Updated team instructions (${scope})`);
|
|
24002
|
-
} catch {
|
|
24555
|
+
} catch (err) {
|
|
24556
|
+
instructionFailedAdapters.add(adapter2.slug);
|
|
24557
|
+
console.warn(` [${adapter2.name}] Failed team instructions (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
24558
|
+
}
|
|
24003
24559
|
}
|
|
24004
24560
|
}
|
|
24005
24561
|
if (agentConfigs) {
|
|
@@ -24020,7 +24576,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
24020
24576
|
agentConfigsApplied++;
|
|
24021
24577
|
const configKeys = Object.keys(configWithoutInstructions).join(", ");
|
|
24022
24578
|
vlog(` [${adapter2.name}] Updated agent config: ${configKeys} (${scope})`);
|
|
24023
|
-
} catch {
|
|
24579
|
+
} catch (err) {
|
|
24580
|
+
configFailedAdapters.add(adapter2.slug);
|
|
24581
|
+
console.warn(` [${adapter2.name}] Failed agent config (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
24582
|
+
}
|
|
24024
24583
|
}
|
|
24025
24584
|
}
|
|
24026
24585
|
}
|
|
@@ -24102,7 +24661,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
24102
24661
|
}
|
|
24103
24662
|
if (team)
|
|
24104
24663
|
agentConfigsApplied++;
|
|
24105
|
-
} catch {
|
|
24664
|
+
} catch (err) {
|
|
24665
|
+
configFailedAdapters.add(adapter2.slug);
|
|
24666
|
+
console.warn(` [${adapter2.name}] Failed agent config: ${err instanceof Error ? err.message : err}`);
|
|
24667
|
+
}
|
|
24106
24668
|
}
|
|
24107
24669
|
state.agentDefaultsVersion = AGENT_DEFAULTS_SCHEMA_VERSION;
|
|
24108
24670
|
}
|
|
@@ -24118,6 +24680,21 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
24118
24680
|
}
|
|
24119
24681
|
const prevMcpNames = state.mcpServers ?? [];
|
|
24120
24682
|
state.lastSyncAt = new Date().toISOString();
|
|
24683
|
+
const lateFailures = [
|
|
24684
|
+
[configFailedAdapters, "config"],
|
|
24685
|
+
[instructionFailedAdapters, "instructions"],
|
|
24686
|
+
[hookFailedAdapters, "hooks"]
|
|
24687
|
+
];
|
|
24688
|
+
for (const [slugs, dimension] of lateFailures) {
|
|
24689
|
+
for (const slug of slugs) {
|
|
24690
|
+
const existing = agentOutcomes[slug];
|
|
24691
|
+
if (existing?.failed?.includes(dimension))
|
|
24692
|
+
continue;
|
|
24693
|
+
const failed = [...existing?.failed ?? [], dimension];
|
|
24694
|
+
agentOutcomes[slug] = { at: existing?.at ?? syncStartedAt, ok: false, failed };
|
|
24695
|
+
}
|
|
24696
|
+
}
|
|
24697
|
+
state.lastSyncAgents = agentOutcomes;
|
|
24121
24698
|
state.mcpServers = mcpEntries.map((e) => e.name);
|
|
24122
24699
|
state.skills = remoteSkills.map((s) => s.name);
|
|
24123
24700
|
state.skillFilenames = [
|
|
@@ -24179,7 +24756,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
24179
24756
|
for (const [name, hash] of Object.entries(newHashes)) {
|
|
24180
24757
|
mergedHashes[name] = hash;
|
|
24181
24758
|
}
|
|
24759
|
+
const retryDeletions = new Set(failedDeletions);
|
|
24182
24760
|
for (const del of plan.deletions) {
|
|
24761
|
+
if (retryDeletions.has(del.name))
|
|
24762
|
+
continue;
|
|
24183
24763
|
delete mergedHashes[del.name];
|
|
24184
24764
|
}
|
|
24185
24765
|
state.skillHashes = mergedHashes;
|
|
@@ -24700,10 +25280,11 @@ var buildPluginCommand = new Command29("build-plugin").description("Build an ins
|
|
|
24700
25280
|
|
|
24701
25281
|
// src/commands/uninstall.ts
|
|
24702
25282
|
init_prompt();
|
|
25283
|
+
init_subprocess();
|
|
24703
25284
|
await init_detect();
|
|
24704
25285
|
import { Command as Command30 } from "commander";
|
|
24705
|
-
import { existsSync as existsSync55, readFileSync as readFileSync44, rmSync as
|
|
24706
|
-
import { join as join52 } from "path";
|
|
25286
|
+
import { existsSync as existsSync55, readFileSync as readFileSync44, writeFileSync as writeFileSync32, readdirSync as readdirSync16, rmSync as rmSync12, unlinkSync as unlinkSync9, lstatSync, readlinkSync } from "fs";
|
|
25287
|
+
import { join as join52, resolve as resolve4, relative as relative5, isAbsolute as isAbsolute5 } from "path";
|
|
24707
25288
|
import { homedir as homedir31 } from "os";
|
|
24708
25289
|
function loadSetupState4(filePath) {
|
|
24709
25290
|
if (!existsSync55(filePath))
|
|
@@ -24714,6 +25295,125 @@ function loadSetupState4(filePath) {
|
|
|
24714
25295
|
return null;
|
|
24715
25296
|
}
|
|
24716
25297
|
}
|
|
25298
|
+
var PRESERVED_ENTRIES = ["bin", "apps", "trash"];
|
|
25299
|
+
function removeRunworkState(stateDir, opts) {
|
|
25300
|
+
const result = { removed: [], preserved: [], errors: [] };
|
|
25301
|
+
if (!existsSync55(stateDir))
|
|
25302
|
+
return result;
|
|
25303
|
+
const preserve = new Set(PRESERVED_ENTRIES);
|
|
25304
|
+
if (opts.keepAuth)
|
|
25305
|
+
preserve.add(".credentials");
|
|
25306
|
+
let entries;
|
|
25307
|
+
try {
|
|
25308
|
+
entries = readdirSync16(stateDir);
|
|
25309
|
+
} catch (err) {
|
|
25310
|
+
result.errors.push(`${stateDir}: ${err instanceof Error ? err.message : err}`);
|
|
25311
|
+
return result;
|
|
25312
|
+
}
|
|
25313
|
+
for (const entry of entries) {
|
|
25314
|
+
const target = join52(stateDir, entry);
|
|
25315
|
+
if (preserve.has(entry)) {
|
|
25316
|
+
if (existsSync55(target))
|
|
25317
|
+
result.preserved.push(target);
|
|
25318
|
+
continue;
|
|
25319
|
+
}
|
|
25320
|
+
try {
|
|
25321
|
+
rmSync12(target, { recursive: true, force: true });
|
|
25322
|
+
result.removed.push(target);
|
|
25323
|
+
} catch (err) {
|
|
25324
|
+
result.errors.push(`${target}: ${err instanceof Error ? err.message : err}`);
|
|
25325
|
+
}
|
|
25326
|
+
}
|
|
25327
|
+
return result;
|
|
25328
|
+
}
|
|
25329
|
+
var SHELL_PROFILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile", ".profile"];
|
|
25330
|
+
var BIN_DIR_PATTERN = /\.runwork[\\/]bin/;
|
|
25331
|
+
function stripRunworkPathLines(file) {
|
|
25332
|
+
if (!existsSync55(file))
|
|
25333
|
+
return false;
|
|
25334
|
+
let content;
|
|
25335
|
+
try {
|
|
25336
|
+
content = readFileSync44(file, "utf-8");
|
|
25337
|
+
} catch {
|
|
25338
|
+
return false;
|
|
25339
|
+
}
|
|
25340
|
+
if (!content.includes("Added by Runwork"))
|
|
25341
|
+
return false;
|
|
25342
|
+
const lines = content.split(`
|
|
25343
|
+
`);
|
|
25344
|
+
const kept = [];
|
|
25345
|
+
for (let i = 0;i < lines.length; i++) {
|
|
25346
|
+
if (/^\s*#\s*Added by Runwork\b/.test(lines[i])) {
|
|
25347
|
+
if (i + 1 < lines.length && BIN_DIR_PATTERN.test(lines[i + 1]))
|
|
25348
|
+
i++;
|
|
25349
|
+
continue;
|
|
25350
|
+
}
|
|
25351
|
+
kept.push(lines[i]);
|
|
25352
|
+
}
|
|
25353
|
+
const next = kept.join(`
|
|
25354
|
+
`);
|
|
25355
|
+
if (next === content)
|
|
25356
|
+
return false;
|
|
25357
|
+
try {
|
|
25358
|
+
writeFileSync32(file, next);
|
|
25359
|
+
return true;
|
|
25360
|
+
} catch {
|
|
25361
|
+
return false;
|
|
25362
|
+
}
|
|
25363
|
+
}
|
|
25364
|
+
function cleanShellProfilePathEntries() {
|
|
25365
|
+
return SHELL_PROFILES.map((name) => join52(homedir31(), name)).filter(stripRunworkPathLines);
|
|
25366
|
+
}
|
|
25367
|
+
function cleanPowerShellProfilePathEntries() {
|
|
25368
|
+
if (process.platform !== "win32")
|
|
25369
|
+
return [];
|
|
25370
|
+
const touched = [];
|
|
25371
|
+
const seen = new Set;
|
|
25372
|
+
for (const host of ["powershell", "pwsh"]) {
|
|
25373
|
+
let profilePath;
|
|
25374
|
+
try {
|
|
25375
|
+
profilePath = execFileSync(host, ["-NoProfile", "-Command", "$PROFILE.CurrentUserCurrentHost"], {
|
|
25376
|
+
encoding: "utf-8",
|
|
25377
|
+
stdio: "pipe"
|
|
25378
|
+
}).trim();
|
|
25379
|
+
} catch {
|
|
25380
|
+
continue;
|
|
25381
|
+
}
|
|
25382
|
+
if (!profilePath || seen.has(profilePath))
|
|
25383
|
+
continue;
|
|
25384
|
+
seen.add(profilePath);
|
|
25385
|
+
if (stripRunworkPathLines(profilePath))
|
|
25386
|
+
touched.push(profilePath);
|
|
25387
|
+
}
|
|
25388
|
+
return touched;
|
|
25389
|
+
}
|
|
25390
|
+
function removeRunworkSymlink(linkPath) {
|
|
25391
|
+
let stat;
|
|
25392
|
+
try {
|
|
25393
|
+
stat = lstatSync(linkPath);
|
|
25394
|
+
} catch {
|
|
25395
|
+
return false;
|
|
25396
|
+
}
|
|
25397
|
+
if (!stat.isSymbolicLink())
|
|
25398
|
+
return false;
|
|
25399
|
+
let target;
|
|
25400
|
+
try {
|
|
25401
|
+
target = resolve4(linkPath, "..", readlinkSync(linkPath));
|
|
25402
|
+
} catch {
|
|
25403
|
+
return false;
|
|
25404
|
+
}
|
|
25405
|
+
const ours = resolve4(join52(homedir31(), ".runwork", "bin"));
|
|
25406
|
+
const rel = relative5(ours, resolve4(target));
|
|
25407
|
+
const insideOurs = rel === "" || !rel.startsWith("..") && !isAbsolute5(rel);
|
|
25408
|
+
if (!insideOurs)
|
|
25409
|
+
return false;
|
|
25410
|
+
try {
|
|
25411
|
+
unlinkSync9(linkPath);
|
|
25412
|
+
return true;
|
|
25413
|
+
} catch {
|
|
25414
|
+
return false;
|
|
25415
|
+
}
|
|
25416
|
+
}
|
|
24717
25417
|
var uninstallCommand = new Command30("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) => {
|
|
24718
25418
|
const projectStatePath = join52(process.cwd(), ".runwork", "setup.json");
|
|
24719
25419
|
const userStatePath = join52(homedir31(), ".runwork", "setup.json");
|
|
@@ -24754,6 +25454,13 @@ This will remove all Runwork configuration from your local agents:
|
|
|
24754
25454
|
} else {
|
|
24755
25455
|
console.log(" - Setup state and auth credentials (~/.runwork/)");
|
|
24756
25456
|
}
|
|
25457
|
+
console.log(" - The PATH line we added to your shell profiles");
|
|
25458
|
+
console.log(" - Symlinks that point at our own binary");
|
|
25459
|
+
console.log(`
|
|
25460
|
+
What will be KEPT:`);
|
|
25461
|
+
console.log(" - ~/.runwork/bin the CLI binary itself");
|
|
25462
|
+
console.log(" - ~/.runwork/apps your own app source");
|
|
25463
|
+
console.log(" - ~/.runwork/trash recoverable copies of removed files");
|
|
24757
25464
|
console.log("");
|
|
24758
25465
|
if (!opts.yes) {
|
|
24759
25466
|
const confirmed = await promptConfirm("Proceed with uninstall?");
|
|
@@ -24772,7 +25479,7 @@ This will remove all Runwork configuration from your local agents:
|
|
|
24772
25479
|
for (const { state, label } of entries) {
|
|
24773
25480
|
const scopes = state.scope === "both" ? ["project", "user"] : [state.scope];
|
|
24774
25481
|
const manifest = {
|
|
24775
|
-
skillFilenames: state.skillFilenames ?? state.skills.map((s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-")),
|
|
25482
|
+
skillFilenames: state.skillFilenames ?? (state.skills ?? []).map((s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-")),
|
|
24776
25483
|
mcpServerNames: state.mcpServers ?? []
|
|
24777
25484
|
};
|
|
24778
25485
|
for (const slug of state.configuredAgents) {
|
|
@@ -24797,27 +25504,50 @@ This will remove all Runwork configuration from your local agents:
|
|
|
24797
25504
|
}
|
|
24798
25505
|
}
|
|
24799
25506
|
const stateDir = label === "project" ? join52(process.cwd(), ".runwork") : join52(homedir31(), ".runwork");
|
|
24800
|
-
if (
|
|
24801
|
-
const
|
|
24802
|
-
|
|
24803
|
-
|
|
24804
|
-
|
|
24805
|
-
|
|
24806
|
-
} catch (err) {
|
|
24807
|
-
console.warn(` Failed to remove ${setupFile}: ${err instanceof Error ? err.message : err}`);
|
|
24808
|
-
errors++;
|
|
24809
|
-
}
|
|
25507
|
+
if (existsSync55(stateDir)) {
|
|
25508
|
+
const outcome = removeRunworkState(stateDir, {
|
|
25509
|
+
keepAuth: Boolean(opts.keepAuth) && label === "user"
|
|
25510
|
+
});
|
|
25511
|
+
if (outcome.removed.length > 0) {
|
|
25512
|
+
console.log(` Removed Runwork state from ${stateDir}`);
|
|
24810
25513
|
}
|
|
24811
|
-
|
|
24812
|
-
|
|
24813
|
-
rmSync13(stateDir, { recursive: true, force: true });
|
|
24814
|
-
console.log(` Removed ${stateDir}`);
|
|
24815
|
-
} catch (err) {
|
|
24816
|
-
console.warn(` Failed to remove ${stateDir}: ${err instanceof Error ? err.message : err}`);
|
|
25514
|
+
for (const problem of outcome.errors) {
|
|
25515
|
+
console.warn(` Failed to remove ${problem}`);
|
|
24817
25516
|
errors++;
|
|
24818
25517
|
}
|
|
25518
|
+
if (outcome.preserved.length > 0) {
|
|
25519
|
+
console.log("");
|
|
25520
|
+
console.log(" Kept (not Runwork's to delete):");
|
|
25521
|
+
for (const kept of outcome.preserved)
|
|
25522
|
+
console.log(` - ${kept}`);
|
|
25523
|
+
}
|
|
24819
25524
|
}
|
|
24820
25525
|
}
|
|
25526
|
+
const touchedProfiles = [
|
|
25527
|
+
...cleanShellProfilePathEntries(),
|
|
25528
|
+
...cleanPowerShellProfilePathEntries()
|
|
25529
|
+
];
|
|
25530
|
+
if (touchedProfiles.length > 0) {
|
|
25531
|
+
console.log("");
|
|
25532
|
+
console.log(" Removed the Runwork PATH line from:");
|
|
25533
|
+
for (const file of touchedProfiles)
|
|
25534
|
+
console.log(` - ${file}`);
|
|
25535
|
+
}
|
|
25536
|
+
const removedLinks = [
|
|
25537
|
+
join52(homedir31(), ".local", "bin", "runwork"),
|
|
25538
|
+
"/usr/local/bin/runwork"
|
|
25539
|
+
].filter(removeRunworkSymlink);
|
|
25540
|
+
if (removedLinks.length > 0) {
|
|
25541
|
+
console.log("");
|
|
25542
|
+
console.log(" Removed symlinks:");
|
|
25543
|
+
for (const link of removedLinks)
|
|
25544
|
+
console.log(` - ${link}`);
|
|
25545
|
+
}
|
|
25546
|
+
if (process.platform === "win32") {
|
|
25547
|
+
console.log("");
|
|
25548
|
+
console.log(" Still on your PATH (remove by hand if you want it gone):");
|
|
25549
|
+
console.log(` ${join52(homedir31(), ".runwork", "bin")} in your user PATH`);
|
|
25550
|
+
}
|
|
24821
25551
|
console.log("");
|
|
24822
25552
|
if (errors > 0) {
|
|
24823
25553
|
console.log(`Uninstall completed with ${errors} warning${errors > 1 ? "s" : ""}. ${cleanedAgents} agent${cleanedAgents > 1 ? "s" : ""} cleaned.`);
|
|
@@ -25041,6 +25771,7 @@ import { Command as Command34 } from "commander";
|
|
|
25041
25771
|
|
|
25042
25772
|
// src/health/checks.ts
|
|
25043
25773
|
init_subprocess();
|
|
25774
|
+
init_http();
|
|
25044
25775
|
init_store();
|
|
25045
25776
|
init_client();
|
|
25046
25777
|
init_http();
|
|
@@ -25242,9 +25973,20 @@ async function checkAuthAndNetwork(ctx) {
|
|
|
25242
25973
|
network: { name: "network", status: "pass", message: `API reachable (${elapsed}ms)` }
|
|
25243
25974
|
};
|
|
25244
25975
|
}
|
|
25976
|
+
const proxy = proxyForUrl(ctx.credentials.baseUrl || BASE_URL2);
|
|
25977
|
+
const network = proxy ? {
|
|
25978
|
+
name: "network",
|
|
25979
|
+
status: "fail",
|
|
25980
|
+
message: `API unreachable via ${proxy.variable} (${proxy.value}): ${message}`,
|
|
25981
|
+
details: [
|
|
25982
|
+
`A proxy is configured through ${proxy.variable}.`,
|
|
25983
|
+
"Runwork routes requests through `curl` when a proxy is set, so curl must be installed and able to reach the proxy.",
|
|
25984
|
+
"If this host should bypass the proxy, add it to NO_PROXY."
|
|
25985
|
+
]
|
|
25986
|
+
} : { name: "network", status: "fail", message: `API unreachable: ${message}` };
|
|
25245
25987
|
return {
|
|
25246
25988
|
auth: { name: "auth", status: "skip", message: "could not verify (network error)" },
|
|
25247
|
-
network
|
|
25989
|
+
network
|
|
25248
25990
|
};
|
|
25249
25991
|
}
|
|
25250
25992
|
}
|
|
@@ -25572,6 +26314,7 @@ async function checkAgentSetup() {
|
|
|
25572
26314
|
details.push(`${state.configuredAgents.length} agent(s) configured`);
|
|
25573
26315
|
}
|
|
25574
26316
|
let mcpChecked = false;
|
|
26317
|
+
let mcpAllHealthy = true;
|
|
25575
26318
|
for (const slug of state.configuredAgents) {
|
|
25576
26319
|
const adapter2 = getAdapterBySlug(slug);
|
|
25577
26320
|
if (!adapter2 || !adapter2.supportsMcpScope("user"))
|
|
@@ -25584,19 +26327,22 @@ async function checkAgentSetup() {
|
|
|
25584
26327
|
if (missingMcp.length > 0) {
|
|
25585
26328
|
details.push(`${missingMcp.length} MCP server(s) missing from ${slug} config`);
|
|
25586
26329
|
upgrade("warn");
|
|
25587
|
-
|
|
25588
|
-
details.push(`${state.mcpServers.length} MCP server(s) configured`);
|
|
26330
|
+
mcpAllHealthy = false;
|
|
25589
26331
|
}
|
|
25590
26332
|
mcpChecked = true;
|
|
25591
|
-
break;
|
|
25592
26333
|
} catch {}
|
|
25593
26334
|
}
|
|
25594
26335
|
}
|
|
26336
|
+
if (mcpChecked && mcpAllHealthy && state.mcpServers.length > 0) {
|
|
26337
|
+
details.push(`${state.mcpServers.length} MCP server(s) configured`);
|
|
26338
|
+
}
|
|
25595
26339
|
if (!mcpChecked && state.mcpServers.length > 0) {
|
|
25596
26340
|
details.push("could not verify MCP servers");
|
|
25597
26341
|
upgrade("warn");
|
|
25598
26342
|
}
|
|
25599
26343
|
let skillsChecked = false;
|
|
26344
|
+
let skillsAllHealthy = true;
|
|
26345
|
+
let skillsSummaryLine = null;
|
|
25600
26346
|
for (const slug of state.configuredAgents) {
|
|
25601
26347
|
const skillsDir = getSkillsDir(slug, "user");
|
|
25602
26348
|
if (!skillsDir)
|
|
@@ -25613,13 +26359,16 @@ async function checkAgentSetup() {
|
|
|
25613
26359
|
if (missingSkills.length > 0) {
|
|
25614
26360
|
details.push(`${missingSkills.length} skill(s) missing from ${slug}`);
|
|
25615
26361
|
upgrade("warn");
|
|
25616
|
-
|
|
26362
|
+
skillsAllHealthy = false;
|
|
26363
|
+
} else if (state.skills.length > 0 && !skillsSummaryLine) {
|
|
25617
26364
|
const mcpCoveredCount = state.skills.filter(isCoveredByMcp).length;
|
|
25618
26365
|
const onDiskCount = state.skills.length - mcpCoveredCount;
|
|
25619
|
-
|
|
26366
|
+
skillsSummaryLine = mcpCoveredCount > 0 ? `${state.skills.length} skill(s) installed (${onDiskCount} on disk, ${mcpCoveredCount} via MCP)` : `${state.skills.length} skill(s) installed`;
|
|
25620
26367
|
}
|
|
25621
26368
|
skillsChecked = true;
|
|
25622
|
-
|
|
26369
|
+
}
|
|
26370
|
+
if (skillsChecked && skillsAllHealthy && skillsSummaryLine) {
|
|
26371
|
+
details.push(skillsSummaryLine);
|
|
25623
26372
|
}
|
|
25624
26373
|
if (!skillsChecked && state.skills.length > 0) {
|
|
25625
26374
|
details.push("could not verify skills");
|
|
@@ -25794,7 +26543,7 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
25794
26543
|
}
|
|
25795
26544
|
|
|
25796
26545
|
// src/agents/runtime-detection.ts
|
|
25797
|
-
import { existsSync as existsSync58, readFileSync as readFileSync47, statSync as
|
|
26546
|
+
import { existsSync as existsSync58, readFileSync as readFileSync47, statSync as statSync11, readdirSync as readdirSync17 } from "fs";
|
|
25798
26547
|
import { homedir as homedir33 } from "os";
|
|
25799
26548
|
import { join as join55 } from "path";
|
|
25800
26549
|
var RUNWORK_SESSIONS_DIR = join55(homedir33(), ".runwork", "sessions");
|
|
@@ -25877,7 +26626,7 @@ function findClaudeCodeSessionFile(sessionId) {
|
|
|
25877
26626
|
return null;
|
|
25878
26627
|
let projectDirs;
|
|
25879
26628
|
try {
|
|
25880
|
-
projectDirs =
|
|
26629
|
+
projectDirs = readdirSync17(root);
|
|
25881
26630
|
} catch {
|
|
25882
26631
|
return null;
|
|
25883
26632
|
}
|
|
@@ -25897,7 +26646,7 @@ function findCodexRolloutFile(threadId) {
|
|
|
25897
26646
|
const dir = stack.pop();
|
|
25898
26647
|
let entries;
|
|
25899
26648
|
try {
|
|
25900
|
-
entries =
|
|
26649
|
+
entries = readdirSync17(dir);
|
|
25901
26650
|
} catch {
|
|
25902
26651
|
continue;
|
|
25903
26652
|
}
|
|
@@ -25905,7 +26654,7 @@ function findCodexRolloutFile(threadId) {
|
|
|
25905
26654
|
const full = join55(dir, entry);
|
|
25906
26655
|
let s;
|
|
25907
26656
|
try {
|
|
25908
|
-
s =
|
|
26657
|
+
s = statSync11(full);
|
|
25909
26658
|
} catch {
|
|
25910
26659
|
continue;
|
|
25911
26660
|
}
|
|
@@ -25924,7 +26673,7 @@ function findNewestClaudeCodeSession() {
|
|
|
25924
26673
|
return null;
|
|
25925
26674
|
let projectDirs;
|
|
25926
26675
|
try {
|
|
25927
|
-
projectDirs =
|
|
26676
|
+
projectDirs = readdirSync17(root);
|
|
25928
26677
|
} catch {
|
|
25929
26678
|
return null;
|
|
25930
26679
|
}
|
|
@@ -25933,7 +26682,7 @@ function findNewestClaudeCodeSession() {
|
|
|
25933
26682
|
const projectPath = join55(root, dir);
|
|
25934
26683
|
let files;
|
|
25935
26684
|
try {
|
|
25936
|
-
files =
|
|
26685
|
+
files = readdirSync17(projectPath);
|
|
25937
26686
|
} catch {
|
|
25938
26687
|
continue;
|
|
25939
26688
|
}
|
|
@@ -25942,7 +26691,7 @@ function findNewestClaudeCodeSession() {
|
|
|
25942
26691
|
continue;
|
|
25943
26692
|
const full = join55(projectPath, file);
|
|
25944
26693
|
try {
|
|
25945
|
-
const s =
|
|
26694
|
+
const s = statSync11(full);
|
|
25946
26695
|
if (!best || s.mtimeMs > best.mtime) {
|
|
25947
26696
|
best = {
|
|
25948
26697
|
sessionId: file.replace(/\.jsonl$/, ""),
|
|
@@ -25967,7 +26716,7 @@ function findNewestCodexRollout() {
|
|
|
25967
26716
|
const dir = stack.pop();
|
|
25968
26717
|
let entries;
|
|
25969
26718
|
try {
|
|
25970
|
-
entries =
|
|
26719
|
+
entries = readdirSync17(dir);
|
|
25971
26720
|
} catch {
|
|
25972
26721
|
continue;
|
|
25973
26722
|
}
|
|
@@ -25975,7 +26724,7 @@ function findNewestCodexRollout() {
|
|
|
25975
26724
|
const full = join55(dir, entry);
|
|
25976
26725
|
let s;
|
|
25977
26726
|
try {
|
|
25978
|
-
s =
|
|
26727
|
+
s = statSync11(full);
|
|
25979
26728
|
} catch {
|
|
25980
26729
|
continue;
|
|
25981
26730
|
}
|
|
@@ -26199,13 +26948,627 @@ var doctorCommand = new Command34("doctor").description("Check system health: au
|
|
|
26199
26948
|
}
|
|
26200
26949
|
});
|
|
26201
26950
|
|
|
26951
|
+
// src/commands/debug.ts
|
|
26952
|
+
init_colors();
|
|
26953
|
+
import { Command as Command35 } from "commander";
|
|
26954
|
+
import { readFileSync as readFileSync48 } from "fs";
|
|
26955
|
+
import { homedir as homedir35, platform as platform7, release, arch, type as osType } from "os";
|
|
26956
|
+
import { join as join58 } from "path";
|
|
26957
|
+
|
|
26958
|
+
// src/debug/capture-plan.ts
|
|
26959
|
+
var CONFIG_MAX_BYTES = 64 * 1024;
|
|
26960
|
+
var LOG_TAIL_BYTES = 64 * 1024;
|
|
26961
|
+
var CENSUS_MAX_ENTRIES = 200;
|
|
26962
|
+
var ENV_NAMES = [
|
|
26963
|
+
"PATH",
|
|
26964
|
+
"SHELL",
|
|
26965
|
+
"HOME",
|
|
26966
|
+
"USERPROFILE",
|
|
26967
|
+
"APPDATA",
|
|
26968
|
+
"LOCALAPPDATA",
|
|
26969
|
+
"XDG_CONFIG_HOME",
|
|
26970
|
+
"HTTP_PROXY",
|
|
26971
|
+
"HTTPS_PROXY",
|
|
26972
|
+
"http_proxy",
|
|
26973
|
+
"https_proxy",
|
|
26974
|
+
"NO_PROXY",
|
|
26975
|
+
"no_proxy",
|
|
26976
|
+
"NODE_EXTRA_CA_CERTS",
|
|
26977
|
+
"RUNWORK_HTTP_TRANSPORT",
|
|
26978
|
+
"RUNWORK_API_URL",
|
|
26979
|
+
"TERM_PROGRAM",
|
|
26980
|
+
"LANG"
|
|
26981
|
+
];
|
|
26982
|
+
function buildCapturePlan(input) {
|
|
26983
|
+
const sep5 = input.sep ?? (input.platform === "win32" ? "\\" : "/");
|
|
26984
|
+
const join56 = (...parts) => parts.join(sep5);
|
|
26985
|
+
const home = input.homeDir.replace(/[/\\]+$/, "");
|
|
26986
|
+
const rw = (...parts) => join56(home, ".runwork", ...parts);
|
|
26987
|
+
const steps = [];
|
|
26988
|
+
steps.push({ id: "env", kind: "env", names: ENV_NAMES, note: "process environment (proxy credentials stripped)" });
|
|
26989
|
+
for (const [id, file] of [
|
|
26990
|
+
["runwork.setup", "setup.json"],
|
|
26991
|
+
["runwork.workspaces", "workspaces.json"],
|
|
26992
|
+
["runwork.reflect-state", "reflect-state.json"],
|
|
26993
|
+
["runwork.reflect-runs", "reflect-runs.json"],
|
|
26994
|
+
["runwork.telemetry-outbox", "telemetry-outbox.json"],
|
|
26995
|
+
["runwork.session-summaries-outbox", "session-summaries-outbox.json"]
|
|
26996
|
+
]) {
|
|
26997
|
+
steps.push({ id, kind: "file", path: rw(file), maxBytes: CONFIG_MAX_BYTES });
|
|
26998
|
+
}
|
|
26999
|
+
for (const [id, file] of [
|
|
27000
|
+
["runwork.conversations", "conversations.json"],
|
|
27001
|
+
["runwork.reflect-queue", "reflect-queue.json"],
|
|
27002
|
+
["runwork.insights", "insights.json"],
|
|
27003
|
+
["runwork.pattern-store", "pattern-store.json"]
|
|
27004
|
+
]) {
|
|
27005
|
+
steps.push({ id, kind: "stat", path: rw(file), note: "size and mtime only: contains conversation text" });
|
|
27006
|
+
}
|
|
27007
|
+
steps.push({ id: "runwork.dir", kind: "census", path: rw(), depth: 1, maxEntries: CENSUS_MAX_ENTRIES }, { id: "runwork.bin", kind: "census", path: rw("bin"), depth: 1, maxEntries: 50, note: "which CLI binaries are installed here" }, { id: "runwork.sessions", kind: "census", path: rw("sessions"), depth: 1, maxEntries: CENSUS_MAX_ENTRIES, note: "written by the agent hooks we install" }, { id: "runwork.log", kind: "file", path: rw("desktop-launch.log"), maxBytes: LOG_TAIL_BYTES, tail: true, note: "tail: this log has no rotation" });
|
|
27008
|
+
for (const [id, ...parts] of [
|
|
27009
|
+
["claude.settings", ".claude", "settings.json"],
|
|
27010
|
+
["claude.md", ".claude", "CLAUDE.md"],
|
|
27011
|
+
["claude.mcp", ".claude", ".mcp.json"],
|
|
27012
|
+
["codex.config", ".codex", "config.toml"],
|
|
27013
|
+
["codex.agents-md", ".codex", "AGENTS.md"],
|
|
27014
|
+
["gemini.settings", ".gemini", "settings.json"]
|
|
27015
|
+
]) {
|
|
27016
|
+
steps.push({ id, kind: "file", path: join56(home, ...parts), maxBytes: CONFIG_MAX_BYTES });
|
|
27017
|
+
}
|
|
27018
|
+
for (const [id, ...parts] of [
|
|
27019
|
+
["claude.skills", ".claude", "skills"],
|
|
27020
|
+
["claude.plugins", ".claude", "plugins"],
|
|
27021
|
+
["agents.skills", ".agents", "skills"],
|
|
27022
|
+
["codex.skills", ".codex", "skills"]
|
|
27023
|
+
]) {
|
|
27024
|
+
steps.push({ id, kind: "census", path: join56(home, ...parts), depth: 1, maxEntries: CENSUS_MAX_ENTRIES });
|
|
27025
|
+
}
|
|
27026
|
+
steps.push({ id: "transcripts.claude-code", kind: "census", path: join56(home, ".claude", "projects"), depth: 1, maxEntries: CENSUS_MAX_ENTRIES }, { id: "transcripts.codex", kind: "census", path: join56(home, ".codex", "sessions"), depth: 2, maxEntries: CENSUS_MAX_ENTRIES }, { id: "transcripts.gemini", kind: "census", path: join56(home, ".gemini", "tmp"), depth: 1, maxEntries: CENSUS_MAX_ENTRIES }, { id: "transcripts.cowork", kind: "census", path: coworkBaseDir(input, join56), depth: 1, maxEntries: CENSUS_MAX_ENTRIES, note: "macOS TCC can refuse this while it still exists" });
|
|
27027
|
+
for (const profile of shellProfiles(input.platform)) {
|
|
27028
|
+
steps.push({
|
|
27029
|
+
id: `shell${profile}`,
|
|
27030
|
+
kind: "stat",
|
|
27031
|
+
path: join56(home, profile),
|
|
27032
|
+
note: "presence only: profile bodies can contain the user's own secrets"
|
|
27033
|
+
});
|
|
27034
|
+
}
|
|
27035
|
+
steps.push(...binaryResolutionSteps(input));
|
|
27036
|
+
if (input.cliPath) {
|
|
27037
|
+
steps.push({ id: "cli.version", kind: "exec", command: input.cliPath, args: ["--version"] }, { id: "cli.doctor", kind: "exec", command: input.cliPath, args: ["--json", "doctor", "-v"] }, { id: "cli.conversations", kind: "exec", command: input.cliPath, args: ["--json", "conversations", "status", "--days", "0"], note: "per-agent scan outcome, no conversation data" });
|
|
27038
|
+
}
|
|
27039
|
+
return steps;
|
|
27040
|
+
}
|
|
27041
|
+
function coworkBaseDir(input, join56) {
|
|
27042
|
+
const home = input.homeDir.replace(/[/\\]+$/, "");
|
|
27043
|
+
if (input.platform === "darwin") {
|
|
27044
|
+
return join56(home, "Library", "Application Support", "Claude", "local-agent-mode-sessions");
|
|
27045
|
+
}
|
|
27046
|
+
if (input.platform === "win32") {
|
|
27047
|
+
const base = input.appData ?? join56(home, "AppData", "Roaming");
|
|
27048
|
+
return join56(base, "Claude", "local-agent-mode-sessions");
|
|
27049
|
+
}
|
|
27050
|
+
return join56(home, ".config", "Claude", "local-agent-mode-sessions");
|
|
27051
|
+
}
|
|
27052
|
+
function shellProfiles(platform7) {
|
|
27053
|
+
if (platform7 === "win32")
|
|
27054
|
+
return [];
|
|
27055
|
+
return [".zshrc", ".zprofile", ".bashrc", ".bash_profile", ".profile"];
|
|
27056
|
+
}
|
|
27057
|
+
function binaryResolutionSteps(input) {
|
|
27058
|
+
const sep5 = input.sep ?? (input.platform === "win32" ? "\\" : "/");
|
|
27059
|
+
const join56 = (...parts) => parts.join(sep5);
|
|
27060
|
+
const home = input.homeDir.replace(/[/\\]+$/, "");
|
|
27061
|
+
if (input.platform === "win32") {
|
|
27062
|
+
return [
|
|
27063
|
+
{ id: "binaries.where", kind: "exec", command: "where.exe", args: ["runwork"], note: "every match, not just the first" },
|
|
27064
|
+
{ id: "binaries.runwork-bin", kind: "stat", path: join56(home, ".runwork", "bin", "runwork.exe") },
|
|
27065
|
+
{ id: "binaries.user-path", kind: "exec", command: "reg.exe", args: ["query", "HKCU\\Environment", "/v", "Path"], note: "the PATH a NEW shell will inherit, not this one" }
|
|
27066
|
+
];
|
|
27067
|
+
}
|
|
27068
|
+
return [
|
|
27069
|
+
{ id: "binaries.which", kind: "exec", command: "which", args: ["-a", "runwork"], note: "every match, not just the first" },
|
|
27070
|
+
{ id: "binaries.runwork-bin", kind: "stat", path: join56(home, ".runwork", "bin", "runwork") },
|
|
27071
|
+
{ id: "binaries.local-bin", kind: "stat", path: join56(home, ".local", "bin", "runwork") },
|
|
27072
|
+
...input.platform === "darwin" ? [{ id: "binaries.usr-local-bin", kind: "stat", path: "/usr/local/bin/runwork" }] : []
|
|
27073
|
+
];
|
|
27074
|
+
}
|
|
27075
|
+
|
|
27076
|
+
// src/debug/capture.ts
|
|
27077
|
+
var CAPTURE_FORMAT = 1;
|
|
27078
|
+
function summarizeBundle(jsonl) {
|
|
27079
|
+
const summary = {
|
|
27080
|
+
formatVersion: CAPTURE_FORMAT,
|
|
27081
|
+
producer: null,
|
|
27082
|
+
stepsPlanned: 0,
|
|
27083
|
+
stepsRun: 0,
|
|
27084
|
+
redactions: 0,
|
|
27085
|
+
withheld: 0,
|
|
27086
|
+
complete: false
|
|
27087
|
+
};
|
|
27088
|
+
for (const line of jsonl.split(`
|
|
27089
|
+
`)) {
|
|
27090
|
+
if (!line.trim())
|
|
27091
|
+
continue;
|
|
27092
|
+
let record;
|
|
27093
|
+
try {
|
|
27094
|
+
record = JSON.parse(line);
|
|
27095
|
+
} catch {
|
|
27096
|
+
continue;
|
|
27097
|
+
}
|
|
27098
|
+
if (record.type === "plan") {
|
|
27099
|
+
summary.formatVersion = record.format;
|
|
27100
|
+
summary.producer = record.producer;
|
|
27101
|
+
summary.stepsPlanned = record.steps.length;
|
|
27102
|
+
} else if (record.type === "step") {
|
|
27103
|
+
summary.stepsRun++;
|
|
27104
|
+
summary.redactions += record.redactions ?? 0;
|
|
27105
|
+
summary.withheld += record.withheld ?? 0;
|
|
27106
|
+
} else if (record.type === "complete") {
|
|
27107
|
+
summary.complete = true;
|
|
27108
|
+
}
|
|
27109
|
+
}
|
|
27110
|
+
return summary;
|
|
27111
|
+
}
|
|
27112
|
+
async function sha256Hex(text2) {
|
|
27113
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text2));
|
|
27114
|
+
return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
27115
|
+
}
|
|
27116
|
+
var NEVER_READ = [".credentials", "credentials.json", "auth.json", ".netrc", "id_rsa"];
|
|
27117
|
+
function isForbiddenPath(path4) {
|
|
27118
|
+
const name = path4.split(/[/\\]/).pop()?.toLowerCase() ?? "";
|
|
27119
|
+
return NEVER_READ.some((deny) => name === deny.toLowerCase());
|
|
27120
|
+
}
|
|
27121
|
+
var SECRET_KEY_NAMES = "token|secret|password|passwd|api[-_]?key|apikey|authorization|access[-_]?token|refresh[-_]?token|client[-_]?secret|private[-_]?key|access[-_]?key|credentials?";
|
|
27122
|
+
var SECRET_KEY_PATTERN = `[A-Za-z0-9_-]*?(?:${SECRET_KEY_NAMES})`;
|
|
27123
|
+
var QUOTED_VALUE = String.raw`"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'`;
|
|
27124
|
+
var SECRET_QUERY_PARAMS = "token|key|secret|password|api[-_]?key|apikey|access[-_]?token|sig|signature";
|
|
27125
|
+
function redactSecrets(text2) {
|
|
27126
|
+
let redactions = 0;
|
|
27127
|
+
const mark = () => {
|
|
27128
|
+
redactions++;
|
|
27129
|
+
return "<redacted>";
|
|
27130
|
+
};
|
|
27131
|
+
const out = text2.replace(new RegExp(`([?&](?:${SECRET_QUERY_PARAMS})=)[^&\\s"'\`]+`, "gi"), (_m, prefix) => `${prefix}${mark()}`).replace(/\b(Bearer\s+)[A-Za-z0-9._~+/=-]{8,}/gi, (_m, prefix) => `${prefix}${mark()}`).replace(new RegExp(`("?${SECRET_KEY_PATTERN}"?\\s*[:=]\\s*)(${QUOTED_VALUE}|[^\\s,}\\]&"']+)`, "gi"), (match, prefix, value) => {
|
|
27132
|
+
if (value.includes("<redacted>"))
|
|
27133
|
+
return match;
|
|
27134
|
+
const quote = value.startsWith('"') ? '"' : value.startsWith("'") ? "'" : "";
|
|
27135
|
+
return `${prefix}${quote}${mark()}${quote}`;
|
|
27136
|
+
});
|
|
27137
|
+
return { text: out, redactions };
|
|
27138
|
+
}
|
|
27139
|
+
var CONTENT_KEY_NAMES = "currentTitle|title|prompt|userMessages|summary|body|snippet";
|
|
27140
|
+
function redactUserContent(text2) {
|
|
27141
|
+
let withheld = 0;
|
|
27142
|
+
const out = text2.replace(new RegExp(`("?\\b(?:${CONTENT_KEY_NAMES})"?\\s*[:=]\\s*)(${QUOTED_VALUE})`, "gi"), (match, prefix, value) => {
|
|
27143
|
+
if (value === '""' || value === "''")
|
|
27144
|
+
return match;
|
|
27145
|
+
withheld++;
|
|
27146
|
+
const quote = value[0];
|
|
27147
|
+
return `${prefix}${quote}<withheld>${quote}`;
|
|
27148
|
+
});
|
|
27149
|
+
return { text: out, withheld };
|
|
27150
|
+
}
|
|
27151
|
+
function redactEnvValue(value) {
|
|
27152
|
+
if (value === null)
|
|
27153
|
+
return null;
|
|
27154
|
+
return value.replace(/(\w+:\/\/)[^/@\s]*@/g, "$1<redacted>@");
|
|
27155
|
+
}
|
|
27156
|
+
function classifyCaptureError(err) {
|
|
27157
|
+
const e = err;
|
|
27158
|
+
const code = e && typeof e === "object" && typeof e.code === "string" ? e.code : undefined;
|
|
27159
|
+
const message = e instanceof Error ? e.message : String(err);
|
|
27160
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
27161
|
+
return { status: "missing", detail: code };
|
|
27162
|
+
if (code === "EACCES" || code === "EPERM")
|
|
27163
|
+
return { status: "denied", detail: code };
|
|
27164
|
+
if (code === "ETIMEDOUT" || code === "TIMEOUT")
|
|
27165
|
+
return { status: "timeout", detail: code };
|
|
27166
|
+
if (code === "ESCOPE")
|
|
27167
|
+
return { status: "skipped", detail: message };
|
|
27168
|
+
return { status: "error", detail: code ? `${code}: ${message}` : message };
|
|
27169
|
+
}
|
|
27170
|
+
function stepTarget(step) {
|
|
27171
|
+
switch (step.kind) {
|
|
27172
|
+
case "file":
|
|
27173
|
+
case "census":
|
|
27174
|
+
case "stat":
|
|
27175
|
+
return step.path;
|
|
27176
|
+
case "env":
|
|
27177
|
+
return step.names.join(",");
|
|
27178
|
+
case "exec":
|
|
27179
|
+
return [step.command, ...step.args].join(" ");
|
|
27180
|
+
}
|
|
27181
|
+
}
|
|
27182
|
+
function withDeadline(work, ms) {
|
|
27183
|
+
return new Promise((resolve5, reject) => {
|
|
27184
|
+
const timer = setTimeout(() => reject(Object.assign(new Error("step deadline"), { code: "TIMEOUT" })), ms);
|
|
27185
|
+
work.then((value) => {
|
|
27186
|
+
clearTimeout(timer);
|
|
27187
|
+
resolve5(value);
|
|
27188
|
+
}, (err) => {
|
|
27189
|
+
clearTimeout(timer);
|
|
27190
|
+
reject(err);
|
|
27191
|
+
});
|
|
27192
|
+
});
|
|
27193
|
+
}
|
|
27194
|
+
async function runStep(port, step, deadlineMs) {
|
|
27195
|
+
const startedMs = port.now();
|
|
27196
|
+
const base = { type: "step", id: step.id, kind: step.kind, target: stepTarget(step) };
|
|
27197
|
+
const done = (extra) => ({ ...base, status: "ok", durationMs: port.now() - startedMs, ...extra });
|
|
27198
|
+
if ((step.kind === "file" || step.kind === "stat") && isForbiddenPath(step.path)) {
|
|
27199
|
+
return { ...base, status: "skipped", detail: "excluded by policy", durationMs: 0 };
|
|
27200
|
+
}
|
|
27201
|
+
try {
|
|
27202
|
+
switch (step.kind) {
|
|
27203
|
+
case "file": {
|
|
27204
|
+
const raw = await withDeadline(port.readText(step.path, step.maxBytes, step.tail === true), deadlineMs);
|
|
27205
|
+
const secrets = redactSecrets(raw.text);
|
|
27206
|
+
const content = redactUserContent(secrets.text);
|
|
27207
|
+
return done({
|
|
27208
|
+
text: content.text,
|
|
27209
|
+
truncated: raw.truncated,
|
|
27210
|
+
...secrets.redactions > 0 ? { redactions: secrets.redactions } : {},
|
|
27211
|
+
...content.withheld > 0 ? { withheld: content.withheld } : {}
|
|
27212
|
+
});
|
|
27213
|
+
}
|
|
27214
|
+
case "census": {
|
|
27215
|
+
const entries = await withDeadline(port.listDir(step.path, step.depth, step.maxEntries), deadlineMs);
|
|
27216
|
+
return done({ entries, entryCount: entries.length });
|
|
27217
|
+
}
|
|
27218
|
+
case "stat": {
|
|
27219
|
+
const info = await withDeadline(port.stat(step.path), deadlineMs);
|
|
27220
|
+
return done({ bytes: info.bytes, modifiedAt: info.modifiedAt });
|
|
27221
|
+
}
|
|
27222
|
+
case "env": {
|
|
27223
|
+
const raw = port.env(step.names);
|
|
27224
|
+
return done({
|
|
27225
|
+
env: Object.fromEntries(Object.entries(raw).map(([k, v]) => [k, redactEnvValue(v)]))
|
|
27226
|
+
});
|
|
27227
|
+
}
|
|
27228
|
+
case "exec": {
|
|
27229
|
+
const result = await withDeadline(port.exec(step.command, step.args), deadlineMs);
|
|
27230
|
+
const out = redactSecrets(result.stdout);
|
|
27231
|
+
const err = redactSecrets(result.stderr);
|
|
27232
|
+
const redactions = out.redactions + err.redactions;
|
|
27233
|
+
return done({
|
|
27234
|
+
text: out.text,
|
|
27235
|
+
exitCode: result.exitCode,
|
|
27236
|
+
stderr: err.text,
|
|
27237
|
+
...redactions > 0 ? { redactions } : {}
|
|
27238
|
+
});
|
|
27239
|
+
}
|
|
27240
|
+
}
|
|
27241
|
+
} catch (err) {
|
|
27242
|
+
const { status, detail } = classifyCaptureError(err);
|
|
27243
|
+
return { ...base, status, detail, durationMs: port.now() - startedMs };
|
|
27244
|
+
}
|
|
27245
|
+
}
|
|
27246
|
+
async function runCapture(port, producer, steps, opts = {}) {
|
|
27247
|
+
const deadlineMs = opts.stepDeadlineMs ?? 1e4;
|
|
27248
|
+
const startedMs = port.now();
|
|
27249
|
+
await port.append({
|
|
27250
|
+
type: "plan",
|
|
27251
|
+
format: CAPTURE_FORMAT,
|
|
27252
|
+
producer,
|
|
27253
|
+
startedAt: new Date(startedMs).toISOString(),
|
|
27254
|
+
steps: steps.map((s) => ({ id: s.id, kind: s.kind, ...s.note ? { note: s.note } : {} }))
|
|
27255
|
+
});
|
|
27256
|
+
let stepsRun = 0;
|
|
27257
|
+
for (const step of steps) {
|
|
27258
|
+
await port.append(await runStep(port, step, deadlineMs));
|
|
27259
|
+
stepsRun++;
|
|
27260
|
+
}
|
|
27261
|
+
if (opts.claims) {
|
|
27262
|
+
try {
|
|
27263
|
+
for (const claim of await opts.claims())
|
|
27264
|
+
await port.append(claim);
|
|
27265
|
+
} catch (err) {
|
|
27266
|
+
await port.append({
|
|
27267
|
+
type: "claim",
|
|
27268
|
+
id: "claims-failed",
|
|
27269
|
+
producer,
|
|
27270
|
+
value: { error: err instanceof Error ? err.message : String(err) }
|
|
27271
|
+
});
|
|
27272
|
+
}
|
|
27273
|
+
}
|
|
27274
|
+
await port.append({
|
|
27275
|
+
type: "complete",
|
|
27276
|
+
finishedAt: new Date(port.now()).toISOString(),
|
|
27277
|
+
stepsPlanned: steps.length,
|
|
27278
|
+
stepsRun,
|
|
27279
|
+
durationMs: port.now() - startedMs
|
|
27280
|
+
});
|
|
27281
|
+
return { stepsRun };
|
|
27282
|
+
}
|
|
27283
|
+
|
|
27284
|
+
// src/utils/machine-id.ts
|
|
27285
|
+
init_atomic_json();
|
|
27286
|
+
import { randomUUID } from "crypto";
|
|
27287
|
+
import { homedir as homedir34 } from "os";
|
|
27288
|
+
import { join as join56 } from "path";
|
|
27289
|
+
function machineIdPath() {
|
|
27290
|
+
return join56(homedir34(), ".runwork", "machine.json");
|
|
27291
|
+
}
|
|
27292
|
+
function isValidId(value) {
|
|
27293
|
+
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
27294
|
+
}
|
|
27295
|
+
function getMachineId() {
|
|
27296
|
+
const path4 = machineIdPath();
|
|
27297
|
+
const existing = readJsonOrNull(path4);
|
|
27298
|
+
if (existing && isValidId(existing.id))
|
|
27299
|
+
return existing.id;
|
|
27300
|
+
const record = { id: randomUUID(), createdAt: new Date().toISOString() };
|
|
27301
|
+
try {
|
|
27302
|
+
writeJsonAtomic(path4, record);
|
|
27303
|
+
} catch {
|
|
27304
|
+
return null;
|
|
27305
|
+
}
|
|
27306
|
+
const settled = readJsonOrNull(path4);
|
|
27307
|
+
return settled && isValidId(settled.id) ? settled.id : record.id;
|
|
27308
|
+
}
|
|
27309
|
+
|
|
27310
|
+
// src/commands/debug.ts
|
|
27311
|
+
init_store();
|
|
27312
|
+
init_client();
|
|
27313
|
+
init_resolve();
|
|
27314
|
+
|
|
27315
|
+
// src/debug/node-port.ts
|
|
27316
|
+
init_subprocess();
|
|
27317
|
+
init_which();
|
|
27318
|
+
import { closeSync as closeSync5, fsyncSync, mkdirSync as mkdirSync29, openSync as openSync5, readSync as readSync3, readdirSync as readdirSync18, statSync as statSync12, writeSync } from "fs";
|
|
27319
|
+
import { dirname as dirname12, join as join57 } from "path";
|
|
27320
|
+
var EXEC_OUTPUT_MAX = 256 * 1024;
|
|
27321
|
+
var EXEC_TIMEOUT_MS = 20000;
|
|
27322
|
+
function truncate5(text2, maxBytes) {
|
|
27323
|
+
return text2.length > maxBytes ? { text: text2.slice(0, maxBytes), truncated: true } : { text: text2, truncated: false };
|
|
27324
|
+
}
|
|
27325
|
+
function entryKind(path4) {
|
|
27326
|
+
try {
|
|
27327
|
+
const s = statSync12(path4);
|
|
27328
|
+
return s.isDirectory() ? "dir" : s.isFile() ? "file" : "other";
|
|
27329
|
+
} catch {
|
|
27330
|
+
return "other";
|
|
27331
|
+
}
|
|
27332
|
+
}
|
|
27333
|
+
function createNodeCapturePort(outputPath) {
|
|
27334
|
+
mkdirSync29(dirname12(outputPath), { recursive: true });
|
|
27335
|
+
const fd = openSync5(outputPath, "w");
|
|
27336
|
+
return {
|
|
27337
|
+
close: () => closeSync5(fd),
|
|
27338
|
+
async append(record) {
|
|
27339
|
+
writeSync(fd, JSON.stringify(record) + `
|
|
27340
|
+
`);
|
|
27341
|
+
try {
|
|
27342
|
+
fsyncSync(fd);
|
|
27343
|
+
} catch {}
|
|
27344
|
+
},
|
|
27345
|
+
async listDir(path4, depth, maxEntries) {
|
|
27346
|
+
const out = [];
|
|
27347
|
+
const queue = [{ dir: path4, prefix: "", level: 0 }];
|
|
27348
|
+
let first = true;
|
|
27349
|
+
while (queue.length > 0 && out.length < maxEntries) {
|
|
27350
|
+
const { dir, prefix, level } = queue.shift();
|
|
27351
|
+
let names;
|
|
27352
|
+
try {
|
|
27353
|
+
names = readdirSync18(dir);
|
|
27354
|
+
} catch (err) {
|
|
27355
|
+
if (first)
|
|
27356
|
+
throw err;
|
|
27357
|
+
continue;
|
|
27358
|
+
} finally {
|
|
27359
|
+
first = false;
|
|
27360
|
+
}
|
|
27361
|
+
for (const name of names) {
|
|
27362
|
+
if (out.length >= maxEntries)
|
|
27363
|
+
break;
|
|
27364
|
+
const full = join57(dir, name);
|
|
27365
|
+
const kind = entryKind(full);
|
|
27366
|
+
let bytes;
|
|
27367
|
+
let modifiedAt;
|
|
27368
|
+
try {
|
|
27369
|
+
const s = statSync12(full);
|
|
27370
|
+
bytes = s.size;
|
|
27371
|
+
modifiedAt = new Date(s.mtimeMs).toISOString();
|
|
27372
|
+
} catch {}
|
|
27373
|
+
out.push({ name: prefix ? `${prefix}/${name}` : name, kind, ...bytes !== undefined ? { bytes } : {}, ...modifiedAt ? { modifiedAt } : {} });
|
|
27374
|
+
if (kind === "dir" && level + 1 < depth) {
|
|
27375
|
+
queue.push({ dir: full, prefix: prefix ? `${prefix}/${name}` : name, level: level + 1 });
|
|
27376
|
+
}
|
|
27377
|
+
}
|
|
27378
|
+
}
|
|
27379
|
+
return out;
|
|
27380
|
+
},
|
|
27381
|
+
async stat(path4) {
|
|
27382
|
+
const s = statSync12(path4);
|
|
27383
|
+
return {
|
|
27384
|
+
bytes: s.size,
|
|
27385
|
+
modifiedAt: new Date(s.mtimeMs).toISOString(),
|
|
27386
|
+
kind: s.isDirectory() ? "dir" : s.isFile() ? "file" : "other"
|
|
27387
|
+
};
|
|
27388
|
+
},
|
|
27389
|
+
async readText(path4, maxBytes, tail) {
|
|
27390
|
+
const s = statSync12(path4);
|
|
27391
|
+
const size = s.size;
|
|
27392
|
+
const length = Math.min(size, maxBytes);
|
|
27393
|
+
const start = tail && size > maxBytes ? size - maxBytes : 0;
|
|
27394
|
+
const buffer = Buffer.alloc(length);
|
|
27395
|
+
const handle = openSync5(path4, "r");
|
|
27396
|
+
try {
|
|
27397
|
+
readSync3(handle, buffer, 0, length, start);
|
|
27398
|
+
} finally {
|
|
27399
|
+
closeSync5(handle);
|
|
27400
|
+
}
|
|
27401
|
+
return { text: buffer.toString("utf-8"), truncated: size > length };
|
|
27402
|
+
},
|
|
27403
|
+
env(names) {
|
|
27404
|
+
return Object.fromEntries(names.map((n) => [n, process.env[n] ?? null]));
|
|
27405
|
+
},
|
|
27406
|
+
async exec(command, args) {
|
|
27407
|
+
const spec = toSpawnSpec(command, args);
|
|
27408
|
+
const result = spawnSync(spec.command, spec.args, {
|
|
27409
|
+
encoding: "utf-8",
|
|
27410
|
+
timeout: EXEC_TIMEOUT_MS,
|
|
27411
|
+
maxBuffer: EXEC_OUTPUT_MAX,
|
|
27412
|
+
...spec.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
27413
|
+
});
|
|
27414
|
+
if (result.error)
|
|
27415
|
+
throw result.error;
|
|
27416
|
+
return {
|
|
27417
|
+
exitCode: result.status ?? -1,
|
|
27418
|
+
stdout: truncate5(result.stdout ?? "", EXEC_OUTPUT_MAX).text,
|
|
27419
|
+
stderr: truncate5(result.stderr ?? "", 8 * 1024).text
|
|
27420
|
+
};
|
|
27421
|
+
},
|
|
27422
|
+
now: () => Date.now()
|
|
27423
|
+
};
|
|
27424
|
+
}
|
|
27425
|
+
|
|
27426
|
+
// src/commands/debug.ts
|
|
27427
|
+
init_transcript_sources();
|
|
27428
|
+
init_which();
|
|
27429
|
+
await init_detect();
|
|
27430
|
+
function defaultOutputPath(now) {
|
|
27431
|
+
const stamp = now.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
|
|
27432
|
+
return join58(homedir35(), ".runwork", "debug", `runwork-debug-${stamp}.jsonl`);
|
|
27433
|
+
}
|
|
27434
|
+
async function cliClaims() {
|
|
27435
|
+
const records = [];
|
|
27436
|
+
records.push({
|
|
27437
|
+
type: "claim",
|
|
27438
|
+
id: "producer.process",
|
|
27439
|
+
producer: "cli",
|
|
27440
|
+
value: {
|
|
27441
|
+
cliVersion: VERSION,
|
|
27442
|
+
execPath: process.execPath,
|
|
27443
|
+
argv0: process.argv[1] ?? null,
|
|
27444
|
+
nodeVersion: process.version,
|
|
27445
|
+
platform: platform7(),
|
|
27446
|
+
osType: osType(),
|
|
27447
|
+
release: release(),
|
|
27448
|
+
arch: arch(),
|
|
27449
|
+
cwd: process.cwd()
|
|
27450
|
+
}
|
|
27451
|
+
});
|
|
27452
|
+
records.push({
|
|
27453
|
+
type: "claim",
|
|
27454
|
+
id: "cli.path-resolution",
|
|
27455
|
+
producer: "cli",
|
|
27456
|
+
derivedFrom: ["binaries.which", "binaries.where", "env"],
|
|
27457
|
+
value: { pathResolved: whichBinary("runwork") }
|
|
27458
|
+
});
|
|
27459
|
+
const adapters = await detectAgents();
|
|
27460
|
+
records.push({
|
|
27461
|
+
type: "claim",
|
|
27462
|
+
id: "agents.detected",
|
|
27463
|
+
producer: "cli",
|
|
27464
|
+
derivedFrom: ["transcripts.claude-code", "transcripts.codex", "transcripts.gemini", "transcripts.cowork"],
|
|
27465
|
+
value: adapters.map((adapter2) => {
|
|
27466
|
+
const sources = adapter2.transcriptSources?.() ?? [];
|
|
27467
|
+
return {
|
|
27468
|
+
slug: adapter2.slug,
|
|
27469
|
+
name: adapter2.name,
|
|
27470
|
+
hasListing: typeof adapter2.listSessions === "function",
|
|
27471
|
+
hasDigests: typeof adapter2.readSessionDigests === "function",
|
|
27472
|
+
transcriptSources: sources.map((s) => s.path),
|
|
27473
|
+
readable: sources.length > 0 ? summarizeTranscriptSources(sources) : null
|
|
27474
|
+
};
|
|
27475
|
+
})
|
|
27476
|
+
});
|
|
27477
|
+
return records;
|
|
27478
|
+
}
|
|
27479
|
+
var debugCommand = new Command35("debug").description("Diagnostics for support");
|
|
27480
|
+
debugCommand.command("capture").description("Collect a diagnostic bundle to a file you can send to support").option("--out <path>", "Where to write the bundle (default: ~/.runwork/debug/)").option("--send", "Also send the bundle to Runwork support (requires sign-in)").option("--note <text>", "What went wrong, in your words. Included with --send").action(async (opts, command) => {
|
|
27481
|
+
const json = command.optsWithGlobals().json === true || !process.stdout.isTTY;
|
|
27482
|
+
const outputPath = typeof opts.out === "string" && opts.out ? opts.out : defaultOutputPath(new Date);
|
|
27483
|
+
const cliPath = whichBinary("runwork");
|
|
27484
|
+
const plan = buildCapturePlan({
|
|
27485
|
+
platform: platform7(),
|
|
27486
|
+
homeDir: homedir35(),
|
|
27487
|
+
appData: process.env.APPDATA ?? null,
|
|
27488
|
+
cliPath
|
|
27489
|
+
});
|
|
27490
|
+
if (!json) {
|
|
27491
|
+
console.log(bold(`
|
|
27492
|
+
Collecting diagnostics`));
|
|
27493
|
+
console.log(dim(` ${plan.length} checks, writing to ${outputPath}`));
|
|
27494
|
+
}
|
|
27495
|
+
const port = createNodeCapturePort(outputPath);
|
|
27496
|
+
let stepsRun = 0;
|
|
27497
|
+
try {
|
|
27498
|
+
({ stepsRun } = await runCapture(port, "cli", plan, { claims: cliClaims }));
|
|
27499
|
+
} finally {
|
|
27500
|
+
port.close();
|
|
27501
|
+
}
|
|
27502
|
+
const sent = opts.send === true ? await sendBundle(outputPath, typeof opts.note === "string" ? opts.note : undefined) : null;
|
|
27503
|
+
if (json) {
|
|
27504
|
+
jsonOut({ path: outputPath, stepsPlanned: plan.length, stepsRun, ...sent ? { sent } : {} });
|
|
27505
|
+
return;
|
|
27506
|
+
}
|
|
27507
|
+
const counted = stepsRun === plan.length ? `${stepsRun}` : `${stepsRun} of ${plan.length}`;
|
|
27508
|
+
console.log(green(`
|
|
27509
|
+
Done. ${counted} checks recorded.`));
|
|
27510
|
+
console.log(`
|
|
27511
|
+
${cyan(outputPath)}
|
|
27512
|
+
`);
|
|
27513
|
+
if (!cliPath) {
|
|
27514
|
+
console.log(yellow("Note: no `runwork` was found on PATH, so the doctor report is not included.\n"));
|
|
27515
|
+
}
|
|
27516
|
+
if (!sent) {
|
|
27517
|
+
console.log(dim("Send that file to support. It contains no credentials and no conversation text."));
|
|
27518
|
+
console.log(dim("Or re-run with --send to upload it."));
|
|
27519
|
+
return;
|
|
27520
|
+
}
|
|
27521
|
+
if (sent.ok) {
|
|
27522
|
+
console.log(green(`Sent to Runwork support (report ${sent.id}).`));
|
|
27523
|
+
console.log(dim(`It is kept until ${sent.expiresAt?.slice(0, 10)} and then deleted.`));
|
|
27524
|
+
} else {
|
|
27525
|
+
console.log(yellow(`Could not send: ${sent.error}`));
|
|
27526
|
+
console.log(dim("The file above is saved and can be sent to support by hand."));
|
|
27527
|
+
}
|
|
27528
|
+
});
|
|
27529
|
+
async function sendBundle(path4, note) {
|
|
27530
|
+
try {
|
|
27531
|
+
const content = readFileSync48(path4, "utf-8");
|
|
27532
|
+
const summary = summarizeBundle(content);
|
|
27533
|
+
const machineId = getMachineId();
|
|
27534
|
+
if (!machineId)
|
|
27535
|
+
return { ok: false, error: "could not identify this machine" };
|
|
27536
|
+
const credentials = getCredentials();
|
|
27537
|
+
if (!credentials)
|
|
27538
|
+
return { ok: false, error: "not signed in; run `runwork login`" };
|
|
27539
|
+
const api = new ApiClient(credentials);
|
|
27540
|
+
const workspace = await resolveWorkspace2(api);
|
|
27541
|
+
const result = await api.createDebugReport(workspace.workspaceId, {
|
|
27542
|
+
machineId,
|
|
27543
|
+
producer: "cli",
|
|
27544
|
+
content,
|
|
27545
|
+
sha256: await sha256Hex(content),
|
|
27546
|
+
formatVersion: summary.formatVersion,
|
|
27547
|
+
...note ? { note } : {},
|
|
27548
|
+
context: {
|
|
27549
|
+
cliVersion: VERSION,
|
|
27550
|
+
platform: platform7(),
|
|
27551
|
+
release: release(),
|
|
27552
|
+
stepsPlanned: summary.stepsPlanned,
|
|
27553
|
+
stepsRun: summary.stepsRun,
|
|
27554
|
+
redactions: summary.redactions,
|
|
27555
|
+
withheld: summary.withheld,
|
|
27556
|
+
complete: summary.complete
|
|
27557
|
+
}
|
|
27558
|
+
});
|
|
27559
|
+
return { ok: true, id: result.id, expiresAt: result.expiresAt };
|
|
27560
|
+
} catch (err) {
|
|
27561
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
27562
|
+
}
|
|
27563
|
+
}
|
|
27564
|
+
|
|
26202
27565
|
// src/commands/share-convo.ts
|
|
26203
27566
|
init_store();
|
|
26204
27567
|
init_client();
|
|
26205
27568
|
init_resolve();
|
|
26206
|
-
import { Command as
|
|
26207
|
-
import { readFileSync as
|
|
26208
|
-
import { join as
|
|
27569
|
+
import { Command as Command36 } from "commander";
|
|
27570
|
+
import { readFileSync as readFileSync49, writeFileSync as writeFileSync33, existsSync as existsSync59, mkdtempSync as mkdtempSync4 } from "fs";
|
|
27571
|
+
import { join as join59 } from "path";
|
|
26209
27572
|
import { tmpdir as tmpdir4 } from "os";
|
|
26210
27573
|
import { createHash as createHash6 } from "crypto";
|
|
26211
27574
|
|
|
@@ -26327,14 +27690,14 @@ function resolveLocalSessionShare(opts, conversation) {
|
|
|
26327
27690
|
process.exit(1);
|
|
26328
27691
|
}
|
|
26329
27692
|
const title = opts.title ?? conversation.title ?? conversation.project;
|
|
26330
|
-
const markdown = renderTranscriptMarkdown(
|
|
27693
|
+
const markdown = renderTranscriptMarkdown(readFileSync49(conversation.transcriptPath, "utf8"), family, title);
|
|
26331
27694
|
if (!markdown) {
|
|
26332
27695
|
console.error("Error: this conversation has no shareable content.");
|
|
26333
27696
|
process.exit(1);
|
|
26334
27697
|
}
|
|
26335
|
-
const tempDir = mkdtempSync4(
|
|
26336
|
-
const transcriptFile =
|
|
26337
|
-
|
|
27698
|
+
const tempDir = mkdtempSync4(join59(tmpdir4(), "runwork-share-"));
|
|
27699
|
+
const transcriptFile = join59(tempDir, "transcript.md");
|
|
27700
|
+
writeFileSync33(transcriptFile, markdown);
|
|
26338
27701
|
opts.transcriptFile = transcriptFile;
|
|
26339
27702
|
opts.nativeFile = opts.nativeFile ?? conversation.transcriptPath;
|
|
26340
27703
|
opts.sourceAgent = opts.sourceAgent ?? conversation.agentSlug;
|
|
@@ -26350,7 +27713,7 @@ function nativeBundleFormatForAgent(slug) {
|
|
|
26350
27713
|
return "codex-rollout";
|
|
26351
27714
|
return null;
|
|
26352
27715
|
}
|
|
26353
|
-
function
|
|
27716
|
+
function sha256Hex2(content) {
|
|
26354
27717
|
return createHash6("sha256").update(content, "utf8").digest("hex");
|
|
26355
27718
|
}
|
|
26356
27719
|
function utf8ByteLength(content) {
|
|
@@ -26392,13 +27755,13 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26392
27755
|
const credentials = requireAuth();
|
|
26393
27756
|
const client = new ApiClient(credentials);
|
|
26394
27757
|
const { workspaceId } = await resolveWorkspace2(client, { workspace: opts.workspace });
|
|
26395
|
-
const transcriptContent =
|
|
27758
|
+
const transcriptContent = readFileSync49(opts.transcriptFile, "utf8");
|
|
26396
27759
|
const bundles = [
|
|
26397
27760
|
{
|
|
26398
27761
|
format: "transcript",
|
|
26399
27762
|
content: transcriptContent,
|
|
26400
27763
|
sizeBytes: utf8ByteLength(transcriptContent),
|
|
26401
|
-
sha256:
|
|
27764
|
+
sha256: sha256Hex2(transcriptContent)
|
|
26402
27765
|
}
|
|
26403
27766
|
];
|
|
26404
27767
|
const detected = detectCurrentAgent();
|
|
@@ -26417,12 +27780,12 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26417
27780
|
const nativeFormat = nativeBundleFormatForAgent(sourceAgent);
|
|
26418
27781
|
if (nativeFormat) {
|
|
26419
27782
|
try {
|
|
26420
|
-
const content =
|
|
27783
|
+
const content = readFileSync49(nativeFilePath, "utf8");
|
|
26421
27784
|
bundles.push({
|
|
26422
27785
|
format: nativeFormat,
|
|
26423
27786
|
content,
|
|
26424
27787
|
sizeBytes: utf8ByteLength(content),
|
|
26425
|
-
sha256:
|
|
27788
|
+
sha256: sha256Hex2(content)
|
|
26426
27789
|
});
|
|
26427
27790
|
} catch (err) {
|
|
26428
27791
|
console.error(`Warning: could not read native file ${nativeFilePath}: ${err instanceof Error ? err.message : err}`);
|
|
@@ -26433,7 +27796,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26433
27796
|
let metadata = {};
|
|
26434
27797
|
if (opts.metadataFile) {
|
|
26435
27798
|
try {
|
|
26436
|
-
metadata = JSON.parse(
|
|
27799
|
+
metadata = JSON.parse(readFileSync49(opts.metadataFile, "utf8"));
|
|
26437
27800
|
} catch (err) {
|
|
26438
27801
|
console.error(`Error: --metadata-file is not valid JSON: ${err instanceof Error ? err.message : err}`);
|
|
26439
27802
|
process.exit(1);
|
|
@@ -26488,18 +27851,18 @@ Skipped: ${result.skipped.map((s) => `${s.identifier} (${s.reason})`).join(", ")
|
|
|
26488
27851
|
process.exit(1);
|
|
26489
27852
|
}
|
|
26490
27853
|
}
|
|
26491
|
-
var shareConvoCommand = new
|
|
27854
|
+
var shareConvoCommand = new Command36("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 unless --session)").option("--session <key>", "Share a LOCAL conversation by registry key (transcript rendered mechanically)").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));
|
|
26492
27855
|
|
|
26493
27856
|
// src/commands/save-convo.ts
|
|
26494
|
-
import { Command as
|
|
26495
|
-
var saveConvoCommand = new
|
|
27857
|
+
import { Command as Command37 } from "commander";
|
|
27858
|
+
var saveConvoCommand = new Command37("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));
|
|
26496
27859
|
|
|
26497
27860
|
// src/commands/inbox.ts
|
|
26498
27861
|
init_store();
|
|
26499
27862
|
init_client();
|
|
26500
27863
|
init_resolve();
|
|
26501
|
-
import { Command as
|
|
26502
|
-
var inboxCommand = new
|
|
27864
|
+
import { Command as Command38 } from "commander";
|
|
27865
|
+
var inboxCommand = new Command38("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) => {
|
|
26503
27866
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
26504
27867
|
const scope = opts.filter === "received" || opts.filter === "sent" || opts.filter === "saved" ? opts.filter : "all";
|
|
26505
27868
|
const limit = opts.limit ? parseInt(opts.limit, 10) : 50;
|
|
@@ -26541,10 +27904,10 @@ init_store();
|
|
|
26541
27904
|
init_client();
|
|
26542
27905
|
init_resolve();
|
|
26543
27906
|
init_registry_data();
|
|
26544
|
-
import { Command as
|
|
26545
|
-
import { writeFileSync as
|
|
26546
|
-
import { homedir as
|
|
26547
|
-
import { join as
|
|
27907
|
+
import { Command as Command39 } from "commander";
|
|
27908
|
+
import { writeFileSync as writeFileSync34, mkdirSync as mkdirSync30, realpathSync } from "fs";
|
|
27909
|
+
import { homedir as homedir36 } from "os";
|
|
27910
|
+
import { join as join60 } from "path";
|
|
26548
27911
|
import { spawn as spawn5 } from "child_process";
|
|
26549
27912
|
init_registry();
|
|
26550
27913
|
init_which();
|
|
@@ -26583,10 +27946,10 @@ function extractCodexUuid(rolloutContent) {
|
|
|
26583
27946
|
}
|
|
26584
27947
|
function placeClaudeJsonl(uuid, content, recipientCwd) {
|
|
26585
27948
|
const encoded = encodeClaudeCodeCwd(recipientCwd);
|
|
26586
|
-
const projectDir =
|
|
26587
|
-
|
|
26588
|
-
const placedAt =
|
|
26589
|
-
|
|
27949
|
+
const projectDir = join60(homedir36(), ".claude", "projects", encoded);
|
|
27950
|
+
mkdirSync30(projectDir, { recursive: true });
|
|
27951
|
+
const placedAt = join60(projectDir, `${uuid}.jsonl`);
|
|
27952
|
+
writeFileSync34(placedAt, content);
|
|
26590
27953
|
return { placedAt, runFromCwd: recipientCwd };
|
|
26591
27954
|
}
|
|
26592
27955
|
function placeCodexRollout(uuid, content) {
|
|
@@ -26594,11 +27957,11 @@ function placeCodexRollout(uuid, content) {
|
|
|
26594
27957
|
const yyyy = String(now.getUTCFullYear());
|
|
26595
27958
|
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
26596
27959
|
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
26597
|
-
const dir =
|
|
26598
|
-
|
|
27960
|
+
const dir = join60(homedir36(), ".codex", "sessions", yyyy, mm, dd);
|
|
27961
|
+
mkdirSync30(dir, { recursive: true });
|
|
26599
27962
|
const ts = now.toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
26600
|
-
const placedAt =
|
|
26601
|
-
|
|
27963
|
+
const placedAt = join60(dir, `rollout-${ts}-${uuid}.jsonl`);
|
|
27964
|
+
writeFileSync34(placedAt, content);
|
|
26602
27965
|
return { placedAt };
|
|
26603
27966
|
}
|
|
26604
27967
|
function pickTargetAgent(opts, sourceAgent) {
|
|
@@ -26617,7 +27980,15 @@ function isAgentInstalled(agent) {
|
|
|
26617
27980
|
}
|
|
26618
27981
|
return false;
|
|
26619
27982
|
}
|
|
26620
|
-
|
|
27983
|
+
function buildResumeCommand(template, uuid, resolvedCli, cliName) {
|
|
27984
|
+
const argv = template.replace(/\{uuid\}/g, uuid).split(" ").filter(Boolean);
|
|
27985
|
+
if (resolvedCli && cliName && argv[0] === cliName) {
|
|
27986
|
+
argv[0] = resolvedCli;
|
|
27987
|
+
}
|
|
27988
|
+
const display = [argv[0]?.includes(" ") ? `"${argv[0]}"` : argv[0], ...argv.slice(1)].filter((part) => Boolean(part)).join(" ");
|
|
27989
|
+
return { argv, display };
|
|
27990
|
+
}
|
|
27991
|
+
var resumeCommand2 = new Command39("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) => {
|
|
26621
27992
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
26622
27993
|
const credentials = requireAuth();
|
|
26623
27994
|
const client = new ApiClient(credentials);
|
|
@@ -26711,12 +28082,8 @@ Bundle placed at: ${placement.placedAt}`);
|
|
|
26711
28082
|
return;
|
|
26712
28083
|
}
|
|
26713
28084
|
if (cap2.mode === "cli-resume") {
|
|
26714
|
-
|
|
26715
|
-
const
|
|
26716
|
-
const cliName = getAgent(target.slug)?.launch?.cli;
|
|
26717
|
-
if (resolvedCli && cliName && resolvedCli !== cliName && cmd.startsWith(`${cliName} `)) {
|
|
26718
|
-
cmd = `"${resolvedCli}" ${cmd.slice(cliName.length + 1)}`;
|
|
26719
|
-
}
|
|
28085
|
+
const { argv, display } = buildResumeCommand(cap2.cliResumeCommand ?? "", nativeUuid, resolveAgentCliCommand(target.slug), getAgent(target.slug)?.launch?.cli);
|
|
28086
|
+
const cmd = display;
|
|
26720
28087
|
const detected = detectCurrentAgent();
|
|
26721
28088
|
const insideSameAgent = detected && detected.slug === target.slug;
|
|
26722
28089
|
const shouldPrintOnly = opts.dryRun || insideSameAgent;
|
|
@@ -26748,10 +28115,11 @@ Or ask the assistant to continue the conversation in THIS session by ` + `fetchi
|
|
|
26748
28115
|
}
|
|
26749
28116
|
return;
|
|
26750
28117
|
}
|
|
26751
|
-
const
|
|
26752
|
-
const child = spawn5(
|
|
28118
|
+
const spec = toSpawnSpec(argv[0], argv.slice(1));
|
|
28119
|
+
const child = spawn5(spec.command, spec.args, {
|
|
26753
28120
|
cwd: placement.runFromCwd ?? recipientCwd,
|
|
26754
|
-
stdio: "inherit"
|
|
28121
|
+
stdio: "inherit",
|
|
28122
|
+
...spec.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
26755
28123
|
});
|
|
26756
28124
|
child.on("exit", (code) => {
|
|
26757
28125
|
process.exit(code ?? 0);
|
|
@@ -26847,7 +28215,7 @@ process.on("uncaughtException", (err) => {
|
|
|
26847
28215
|
console.error(`Uncaught exception: ${formatError(err)}`);
|
|
26848
28216
|
process.exit(1);
|
|
26849
28217
|
});
|
|
26850
|
-
var program = new
|
|
28218
|
+
var program = new Command40;
|
|
26851
28219
|
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)");
|
|
26852
28220
|
program.addCommand(infoCommand);
|
|
26853
28221
|
program.addCommand(loginCommand);
|
|
@@ -26880,6 +28248,7 @@ program.addCommand(appsCommand);
|
|
|
26880
28248
|
program.addCommand(membersCommand);
|
|
26881
28249
|
program.addCommand(apiCommand);
|
|
26882
28250
|
program.addCommand(doctorCommand);
|
|
28251
|
+
program.addCommand(debugCommand);
|
|
26883
28252
|
program.addCommand(shareConvoCommand);
|
|
26884
28253
|
program.addCommand(saveConvoCommand);
|
|
26885
28254
|
program.addCommand(inboxCommand);
|