runwork 0.25.3 → 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 +1204 -142
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -62,7 +62,11 @@ import {
|
|
|
62
62
|
createInflateRaw
|
|
63
63
|
} from "node:zlib";
|
|
64
64
|
function firstEnv(names) {
|
|
65
|
-
|
|
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;
|
|
66
70
|
const value = process.env[variable];
|
|
67
71
|
if (value && value.trim() !== "")
|
|
68
72
|
return { variable, value: value.trim() };
|
|
@@ -828,6 +832,10 @@ class ApiClient {
|
|
|
828
832
|
async reportTelemetry(workspaceId, events) {
|
|
829
833
|
await this.request(`/api/workspaces/${workspaceId}/team/telemetry`, { method: "POST", body: JSON.stringify({ events }) });
|
|
830
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
|
+
}
|
|
831
839
|
async createSharedConversation(workspaceId, body) {
|
|
832
840
|
const res = await this.request(`/api/workspaces/${workspaceId}/shared-conversations`, {
|
|
833
841
|
method: "POST",
|
|
@@ -1038,10 +1046,13 @@ var init_workspace_state = __esm(() => {
|
|
|
1038
1046
|
// src/utils/subprocess.ts
|
|
1039
1047
|
import {
|
|
1040
1048
|
execFileSync as cpExecFileSync,
|
|
1041
|
-
spawn as cpSpawn
|
|
1049
|
+
spawn as cpSpawn,
|
|
1050
|
+
spawnSync as cpSpawnSync
|
|
1042
1051
|
} from "child_process";
|
|
1043
1052
|
var execFileSync = (file, args, options) => {
|
|
1044
1053
|
return cpExecFileSync(file, args, { windowsHide: true, ...options ?? {} });
|
|
1054
|
+
}, spawnSync = (file, args, options) => {
|
|
1055
|
+
return cpSpawnSync(file, args, { windowsHide: true, ...options ?? {} });
|
|
1045
1056
|
}, spawn2 = (command, args, options) => {
|
|
1046
1057
|
return cpSpawn(command, args ?? [], { windowsHide: true, ...options ?? {} });
|
|
1047
1058
|
};
|
|
@@ -3135,13 +3146,56 @@ function bufToStr2(val) {
|
|
|
3135
3146
|
return val.toString("utf-8").trim();
|
|
3136
3147
|
return "";
|
|
3137
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
|
+
}
|
|
3138
3191
|
function syncWithRemote(cwd) {
|
|
3139
3192
|
if (!hasCommits(cwd)) {
|
|
3140
3193
|
return { status: "skipped", pushed: false };
|
|
3141
3194
|
}
|
|
3142
3195
|
const dirty = hasTrackedChanges(cwd);
|
|
3196
|
+
let stashSha = null;
|
|
3143
3197
|
if (dirty) {
|
|
3144
|
-
|
|
3198
|
+
stashSha = pushSyncStash(cwd);
|
|
3145
3199
|
}
|
|
3146
3200
|
let status = "synced";
|
|
3147
3201
|
let syncError;
|
|
@@ -3182,9 +3236,8 @@ function syncWithRemote(cwd) {
|
|
|
3182
3236
|
syncError = extractGitError(fetchErr);
|
|
3183
3237
|
}
|
|
3184
3238
|
if (dirty) {
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
} catch {
|
|
3239
|
+
const restored = restoreSyncStash(cwd, stashSha);
|
|
3240
|
+
if (restored !== "restored") {
|
|
3188
3241
|
return { status, pushed: false, error: "stash-conflict", keptUntracked, remoteOverwrote };
|
|
3189
3242
|
}
|
|
3190
3243
|
}
|
|
@@ -3201,6 +3254,7 @@ function syncWithRemote(cwd) {
|
|
|
3201
3254
|
}
|
|
3202
3255
|
return { status, pushed, pushError, keptUntracked, remoteOverwrote };
|
|
3203
3256
|
}
|
|
3257
|
+
var SYNC_STASH_TAG = "runwork-dev-sync";
|
|
3204
3258
|
var init_sync = __esm(() => {
|
|
3205
3259
|
init_subprocess();
|
|
3206
3260
|
init_manifest();
|
|
@@ -5105,10 +5159,6 @@ export declare class FileStorageClient {
|
|
|
5105
5159
|
*/
|
|
5106
5160
|
export declare function createFileStorageClient(env: Env): FileStorageClient;
|
|
5107
5161
|
import type { Hono } from 'hono';
|
|
5108
|
-
/**
|
|
5109
|
-
* Mount file storage routes on the Hono app
|
|
5110
|
-
* Provides REST API for R2 bucket operations
|
|
5111
|
-
*/
|
|
5112
5162
|
export declare function fileStorageRoutes(app: Hono<{
|
|
5113
5163
|
Bindings: Env;
|
|
5114
5164
|
}>): void;
|
|
@@ -5285,6 +5335,46 @@ export declare function componentRoutes(app: Hono<{
|
|
|
5285
5335
|
`,
|
|
5286
5336
|
"workspace.d.ts": `export { WorkspaceContext, getWorkspaceContext, listWorkspaceEntity, getWorkspaceEntity, createWorkspaceEntity, updateWorkspaceEntity, deleteWorkspaceEntity, callAppEndpoint, initializeWorkspace, } from './core-workspace';
|
|
5287
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;
|
|
5288
5378
|
`,
|
|
5289
5379
|
"core-scheduler.d.ts": `/**
|
|
5290
5380
|
* Core Scheduled Jobs Framework
|
|
@@ -5936,8 +6026,6 @@ export type Doc<T> = {
|
|
|
5936
6026
|
* - Security validation on all field names
|
|
5937
6027
|
*/
|
|
5938
6028
|
export declare class EntityDO extends DurableObject<Env> {
|
|
5939
|
-
ctx: DurableObjectState;
|
|
5940
|
-
env: Env;
|
|
5941
6029
|
private _tableReady;
|
|
5942
6030
|
private _migrationDone;
|
|
5943
6031
|
constructor(ctx: DurableObjectState, env: Env);
|
|
@@ -6942,13 +7030,6 @@ export declare function toChannelName(appName: string): string;
|
|
|
6942
7030
|
* these entries in the observability timeline.
|
|
6943
7031
|
*/
|
|
6944
7032
|
export declare function flog(level: 'info' | 'warn' | 'error', system: string, message: string, data?: Record<string, unknown>): void;
|
|
6945
|
-
/**
|
|
6946
|
-
* Emit an event to the workspace unified event stream.
|
|
6947
|
-
* Fire-and-forget: uses ctx.waitUntil so it doesn't block the response.
|
|
6948
|
-
* Silently skips if workspace env vars are not configured (standalone mode).
|
|
6949
|
-
*
|
|
6950
|
-
* Automatically routes events to a channel derived from APP_NAME when available.
|
|
6951
|
-
*/
|
|
6952
7033
|
export declare function emitEvent(ctx: WaitUntilContext, env: EventEnv, event: EmitEventParams): void;
|
|
6953
7034
|
export {};
|
|
6954
7035
|
`,
|
|
@@ -7023,7 +7104,109 @@ export declare const isStr: (s: unknown) => s is string;
|
|
|
7023
7104
|
* that would cause structured clone to fail in ctx.storage.put().
|
|
7024
7105
|
*/
|
|
7025
7106
|
export declare function safeClone<T>(value: T, fallback?: T): T;
|
|
7026
|
-
|
|
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>;
|
|
7027
7210
|
/**
|
|
7028
7211
|
* Workspace API fetch - routes workspace service requests through WorkspaceObject DO
|
|
7029
7212
|
* for production WfP workers, avoiding 522 recursive invocation errors.
|
|
@@ -7036,7 +7219,7 @@ export declare function platformFetch(env: Env, url: string | URL, init?: Reques
|
|
|
7036
7219
|
* @param path - Workspace API sub-path (e.g., '/ingest-event')
|
|
7037
7220
|
* @param init - Standard fetch options
|
|
7038
7221
|
*/
|
|
7039
|
-
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>;
|
|
7040
7223
|
`,
|
|
7041
7224
|
"workflows.d.ts": `import type { WorkflowDefinition } from './core-workflow-types';
|
|
7042
7225
|
/**
|
|
@@ -7231,7 +7414,7 @@ export declare class BaseAgent extends AIChatAgent<Env> {
|
|
|
7231
7414
|
* - Types are defined in core-workflow-types.ts
|
|
7232
7415
|
* - Native DO-based implementation is in core-workflow-instance.ts and core-workflow-coordinator.ts
|
|
7233
7416
|
*/
|
|
7234
|
-
import type
|
|
7417
|
+
import { type Env } from './core-utils';
|
|
7235
7418
|
export type { WorkflowResult, WorkflowStatus, WorkflowInstanceInfo, WorkflowDefinition, WorkflowContext, WorkflowStepUtilities, StepOptions, WaitEventOptions, WorkflowLogger, WorkflowState, WorkflowStatusResponse, } from './core-workflow-types';
|
|
7236
7419
|
import type { WorkflowInstanceInfo } from './core-workflow-types';
|
|
7237
7420
|
/**
|
|
@@ -7780,8 +7963,8 @@ export declare const integrationApiSchema: z.ZodObject<{
|
|
|
7780
7963
|
endpoint: z.ZodString;
|
|
7781
7964
|
data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
7782
7965
|
}, "strip", z.ZodTypeAny, {
|
|
7783
|
-
method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE";
|
|
7784
7966
|
endpoint: string;
|
|
7967
|
+
method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE";
|
|
7785
7968
|
data?: Record<string, unknown> | undefined;
|
|
7786
7969
|
}, {
|
|
7787
7970
|
endpoint: string;
|
|
@@ -8022,7 +8205,7 @@ function createKeyboardListener() {
|
|
|
8022
8205
|
}
|
|
8023
8206
|
|
|
8024
8207
|
// src/generated/version.ts
|
|
8025
|
-
var VERSION = "0.
|
|
8208
|
+
var VERSION = "0.26.0";
|
|
8026
8209
|
|
|
8027
8210
|
// src/commands/dev.ts
|
|
8028
8211
|
var exports_dev = {};
|
|
@@ -8103,6 +8286,7 @@ async function execDev(options) {
|
|
|
8103
8286
|
await ensureGitCredentialHelper(creds.baseUrl);
|
|
8104
8287
|
}
|
|
8105
8288
|
ensureRunworkRemote(cwd, client.getGitRemoteUrl(config.workspaceId, config.appId));
|
|
8289
|
+
let editsUnprotected = false;
|
|
8106
8290
|
const oldManifest = await loadManifest(cwd);
|
|
8107
8291
|
if (oldManifest) {
|
|
8108
8292
|
const userEdits = await detectUserEdits(cwd, oldManifest);
|
|
@@ -8126,9 +8310,22 @@ async function execDev(options) {
|
|
|
8126
8310
|
execFileSync("git", ["add", "--", ...userEdits], { stdio: "pipe" });
|
|
8127
8311
|
execFileSync("git", ["commit", "-m", "sync: user edits"], { stdio: "pipe" });
|
|
8128
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
|
+
}
|
|
8129
8326
|
}
|
|
8130
8327
|
}
|
|
8131
|
-
if (!noSync) {
|
|
8328
|
+
if (!noSync && !editsUnprotected) {
|
|
8132
8329
|
if (useJson) {
|
|
8133
8330
|
jsonLine({ event: "startup", phase: "template_update", timestamp: ts() });
|
|
8134
8331
|
} else {
|
|
@@ -8971,10 +9168,31 @@ function startAppProbeScript(pattern) {
|
|
|
8971
9168
|
function isCommandNotFoundExit(code) {
|
|
8972
9169
|
return typeof code === "number" && COMMAND_NOT_FOUND_EXIT_CODES.includes(code);
|
|
8973
9170
|
}
|
|
8974
|
-
|
|
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;
|
|
8975
9188
|
var init_detection_probes = __esm(() => {
|
|
8976
9189
|
WINDOWS_PATH_REFRESH = "try { $env:Path = [Environment]::GetEnvironmentVariable('Path','Machine') + ';' + " + "[Environment]::GetEnvironmentVariable('Path','User') + ';' + $env:Path } " + `catch { Write-Output '${PATH_REFRESH_FAILED_MARKER}' }; `;
|
|
8977
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
|
+
};
|
|
8978
9196
|
});
|
|
8979
9197
|
|
|
8980
9198
|
// src/utils/which.ts
|
|
@@ -9477,7 +9695,7 @@ var init_registry_data = __esm(() => {
|
|
|
9477
9695
|
{
|
|
9478
9696
|
method: "path",
|
|
9479
9697
|
target: {
|
|
9480
|
-
windows: "
|
|
9698
|
+
windows: "%APPDATA%/Claude/claude_desktop_config.json",
|
|
9481
9699
|
linux: ".config/Claude/claude_desktop_config.json"
|
|
9482
9700
|
}
|
|
9483
9701
|
}
|
|
@@ -9490,7 +9708,7 @@ var init_registry_data = __esm(() => {
|
|
|
9490
9708
|
skillsPaths: { global: ".claude/skills", project: ".claude/skills" },
|
|
9491
9709
|
mcpConfigPath: {
|
|
9492
9710
|
macos: "Library/Application Support/Claude/claude_desktop_config.json",
|
|
9493
|
-
windows: "
|
|
9711
|
+
windows: "%APPDATA%/Claude/claude_desktop_config.json",
|
|
9494
9712
|
linux: ".config/Claude/claude_desktop_config.json"
|
|
9495
9713
|
},
|
|
9496
9714
|
mcpConfigKey: "mcpServers",
|
|
@@ -9800,8 +10018,8 @@ var init_registry_data = __esm(() => {
|
|
|
9800
10018
|
target: [
|
|
9801
10019
|
{ method: "binary", target: "windsurf" },
|
|
9802
10020
|
{ method: "path", target: { macos: "/Applications/Windsurf.app" } },
|
|
9803
|
-
{ method: "path", target: { windows: "
|
|
9804
|
-
{ method: "path", target: { windows: "
|
|
10021
|
+
{ method: "path", target: { windows: "%LOCALAPPDATA%/Programs/Windsurf" } },
|
|
10022
|
+
{ method: "path", target: { windows: "%ProgramFiles%/Windsurf" } }
|
|
9805
10023
|
]
|
|
9806
10024
|
},
|
|
9807
10025
|
launch: { app: { macos: "Windsurf", windows: "Windsurf" } },
|
|
@@ -9881,7 +10099,14 @@ var init_registry_data = __esm(() => {
|
|
|
9881
10099
|
name: "GitHub Copilot (VS Code)",
|
|
9882
10100
|
description: "GitHub's AI pair programmer in VS Code",
|
|
9883
10101
|
category: "extension",
|
|
9884
|
-
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
|
+
},
|
|
9885
10110
|
launch: { app: { macos: "Visual Studio Code", windows: "Code" }, cli: "code" },
|
|
9886
10111
|
logo: "vscode",
|
|
9887
10112
|
downloadUrl: "https://marketplace.visualstudio.com/items?itemName=GitHub.copilot",
|
|
@@ -11760,7 +11985,7 @@ var verbose = false;
|
|
|
11760
11985
|
import { execFile } from "child_process";
|
|
11761
11986
|
import { existsSync as existsSync31 } from "fs";
|
|
11762
11987
|
import { homedir as homedir8, platform as platform3 } from "os";
|
|
11763
|
-
import {
|
|
11988
|
+
import { join as join25 } from "path";
|
|
11764
11989
|
import { promisify } from "util";
|
|
11765
11990
|
function isWindows() {
|
|
11766
11991
|
return platform3() === "win32";
|
|
@@ -11781,7 +12006,12 @@ function resolveDetectionPath(target) {
|
|
|
11781
12006
|
const resolved = resolvePlatformString(target);
|
|
11782
12007
|
if (!resolved)
|
|
11783
12008
|
return null;
|
|
11784
|
-
|
|
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;
|
|
11785
12015
|
}
|
|
11786
12016
|
function checkPath(target) {
|
|
11787
12017
|
const absolute = resolveDetectionPath(target);
|
|
@@ -11865,8 +12095,70 @@ var init_detection = __esm(() => {
|
|
|
11865
12095
|
NOT_DETECTED = { detected: false };
|
|
11866
12096
|
});
|
|
11867
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
|
+
|
|
11868
12160
|
// src/agents/claude-code.ts
|
|
11869
|
-
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";
|
|
11870
12162
|
import { join as join26 } from "path";
|
|
11871
12163
|
import { homedir as homedir9 } from "os";
|
|
11872
12164
|
function getPluginJson() {
|
|
@@ -11910,6 +12202,7 @@ var init_claude_code = __esm(() => {
|
|
|
11910
12202
|
init_instruction_hint();
|
|
11911
12203
|
init_session_start_hook();
|
|
11912
12204
|
init_detection();
|
|
12205
|
+
init_transcript_sources();
|
|
11913
12206
|
ClaudeCodeAdapter = class ClaudeCodeAdapter extends RegistryDetectedAdapter {
|
|
11914
12207
|
name = "Claude Code";
|
|
11915
12208
|
slug = "claude-code";
|
|
@@ -12207,7 +12500,7 @@ ${instructions}`;
|
|
|
12207
12500
|
const filePath = join26(cwdPath, file);
|
|
12208
12501
|
let stat;
|
|
12209
12502
|
try {
|
|
12210
|
-
stat =
|
|
12503
|
+
stat = statSync5(filePath);
|
|
12211
12504
|
} catch {
|
|
12212
12505
|
continue;
|
|
12213
12506
|
}
|
|
@@ -12299,9 +12592,15 @@ ${instructions}`;
|
|
|
12299
12592
|
return null;
|
|
12300
12593
|
}
|
|
12301
12594
|
}
|
|
12595
|
+
transcriptRoot() {
|
|
12596
|
+
return join26(homedir9(), ".claude", "projects");
|
|
12597
|
+
}
|
|
12598
|
+
transcriptSources() {
|
|
12599
|
+
return [{ path: this.transcriptRoot(), kind: "dir" }];
|
|
12600
|
+
}
|
|
12302
12601
|
async readSessionDigests(sinceISO) {
|
|
12303
12602
|
try {
|
|
12304
|
-
const projectsDir =
|
|
12603
|
+
const projectsDir = this.transcriptRoot();
|
|
12305
12604
|
if (!existsSync32(projectsDir))
|
|
12306
12605
|
return null;
|
|
12307
12606
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
@@ -12326,7 +12625,7 @@ ${instructions}`;
|
|
|
12326
12625
|
const filePath = join26(cwdPath, file);
|
|
12327
12626
|
let stat;
|
|
12328
12627
|
try {
|
|
12329
|
-
stat =
|
|
12628
|
+
stat = statSync5(filePath);
|
|
12330
12629
|
} catch {
|
|
12331
12630
|
continue;
|
|
12332
12631
|
}
|
|
@@ -12350,8 +12649,8 @@ ${instructions}`;
|
|
|
12350
12649
|
}
|
|
12351
12650
|
async listSessions(sinceISO) {
|
|
12352
12651
|
try {
|
|
12353
|
-
const projectsDir =
|
|
12354
|
-
if (!
|
|
12652
|
+
const projectsDir = this.transcriptRoot();
|
|
12653
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
12355
12654
|
return null;
|
|
12356
12655
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
12357
12656
|
let cwdEntries;
|
|
@@ -12375,7 +12674,7 @@ ${instructions}`;
|
|
|
12375
12674
|
const filePath = join26(cwdPath, file);
|
|
12376
12675
|
let stat;
|
|
12377
12676
|
try {
|
|
12378
|
-
stat =
|
|
12677
|
+
stat = statSync5(filePath);
|
|
12379
12678
|
} catch {
|
|
12380
12679
|
continue;
|
|
12381
12680
|
}
|
|
@@ -12413,7 +12712,7 @@ ${instructions}`;
|
|
|
12413
12712
|
}
|
|
12414
12713
|
async readSkillUsage(lastSyncAt) {
|
|
12415
12714
|
try {
|
|
12416
|
-
const projectsDir =
|
|
12715
|
+
const projectsDir = this.transcriptRoot();
|
|
12417
12716
|
if (!existsSync32(projectsDir))
|
|
12418
12717
|
return null;
|
|
12419
12718
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -12448,7 +12747,7 @@ ${instructions}`;
|
|
|
12448
12747
|
const filePath = join26(cwdPath, file);
|
|
12449
12748
|
let fileStat;
|
|
12450
12749
|
try {
|
|
12451
|
-
fileStat =
|
|
12750
|
+
fileStat = statSync5(filePath);
|
|
12452
12751
|
} catch {
|
|
12453
12752
|
continue;
|
|
12454
12753
|
}
|
|
@@ -12704,7 +13003,7 @@ var init_claude_desktop_plugin_tree = __esm(() => {
|
|
|
12704
13003
|
});
|
|
12705
13004
|
|
|
12706
13005
|
// src/agents/claude-desktop.ts
|
|
12707
|
-
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";
|
|
12708
13007
|
import { dirname as dirname9, join as join28 } from "path";
|
|
12709
13008
|
import { homedir as homedir10, platform as platform4, tmpdir as tmpdir3 } from "os";
|
|
12710
13009
|
function isRunworkRpmPluginName(name) {
|
|
@@ -12863,6 +13162,7 @@ function setCoworkPluginEnabled(pluginsDir, enabled) {
|
|
|
12863
13162
|
var PLUGIN_NAME2 = "runwork", PLUGIN_VERSION2 = "1.0.0", PLUGIN_DESCRIPTION = "Skills and tools from your Runwork workspace", PLUGIN_AUTHOR_NAME = "Runwork", ClaudeDesktopAdapter;
|
|
12864
13163
|
var init_claude_desktop = __esm(() => {
|
|
12865
13164
|
init_types();
|
|
13165
|
+
init_transcript_sources();
|
|
12866
13166
|
init_session_digest();
|
|
12867
13167
|
init_json_config();
|
|
12868
13168
|
init_instruction_hint();
|
|
@@ -13136,7 +13436,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
13136
13436
|
const filePath = join28(projPath, file);
|
|
13137
13437
|
let stat;
|
|
13138
13438
|
try {
|
|
13139
|
-
stat =
|
|
13439
|
+
stat = statSync6(filePath);
|
|
13140
13440
|
} catch {
|
|
13141
13441
|
continue;
|
|
13142
13442
|
}
|
|
@@ -13161,10 +13461,13 @@ var init_claude_desktop = __esm(() => {
|
|
|
13161
13461
|
return null;
|
|
13162
13462
|
}
|
|
13163
13463
|
}
|
|
13464
|
+
transcriptSources() {
|
|
13465
|
+
return [{ path: getCoworkBaseDir(), kind: "dir" }];
|
|
13466
|
+
}
|
|
13164
13467
|
async listSessions(sinceISO) {
|
|
13165
13468
|
try {
|
|
13166
13469
|
const baseDir = getCoworkBaseDir();
|
|
13167
|
-
if (!
|
|
13470
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
13168
13471
|
return null;
|
|
13169
13472
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
13170
13473
|
let accounts;
|
|
@@ -13200,7 +13503,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
13200
13503
|
const filePath = join28(projPath, file);
|
|
13201
13504
|
let stat;
|
|
13202
13505
|
try {
|
|
13203
|
-
stat =
|
|
13506
|
+
stat = statSync6(filePath);
|
|
13204
13507
|
} catch {
|
|
13205
13508
|
continue;
|
|
13206
13509
|
}
|
|
@@ -13341,7 +13644,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
13341
13644
|
continue;
|
|
13342
13645
|
const orgPath = join28(baseDir, orgDir);
|
|
13343
13646
|
try {
|
|
13344
|
-
if (!
|
|
13647
|
+
if (!statSync6(orgPath).isDirectory())
|
|
13345
13648
|
continue;
|
|
13346
13649
|
} catch {
|
|
13347
13650
|
continue;
|
|
@@ -13351,7 +13654,7 @@ var init_claude_desktop = __esm(() => {
|
|
|
13351
13654
|
continue;
|
|
13352
13655
|
const userPath = join28(orgPath, userDir);
|
|
13353
13656
|
try {
|
|
13354
|
-
if (!
|
|
13657
|
+
if (!statSync6(userPath).isDirectory())
|
|
13355
13658
|
continue;
|
|
13356
13659
|
} catch {
|
|
13357
13660
|
continue;
|
|
@@ -13479,6 +13782,7 @@ var init_cursor = __esm(async () => {
|
|
|
13479
13782
|
init_skill_removal();
|
|
13480
13783
|
init_json_config();
|
|
13481
13784
|
init_detection();
|
|
13785
|
+
init_transcript_sources();
|
|
13482
13786
|
await init_sqlite();
|
|
13483
13787
|
CursorAdapter = class CursorAdapter extends RegistryDetectedAdapter {
|
|
13484
13788
|
name = "Cursor";
|
|
@@ -13737,6 +14041,16 @@ ${instructions}`;
|
|
|
13737
14041
|
return null;
|
|
13738
14042
|
}
|
|
13739
14043
|
}
|
|
14044
|
+
transcriptSources() {
|
|
14045
|
+
return [
|
|
14046
|
+
{
|
|
14047
|
+
path: this.globalStorageDbPath(),
|
|
14048
|
+
kind: "file",
|
|
14049
|
+
tool: "sqlite",
|
|
14050
|
+
available: sqliteAvailable
|
|
14051
|
+
}
|
|
14052
|
+
];
|
|
14053
|
+
}
|
|
13740
14054
|
globalStorageDbPath() {
|
|
13741
14055
|
const os2 = platform5();
|
|
13742
14056
|
if (os2 === "darwin") {
|
|
@@ -13764,7 +14078,7 @@ ${instructions}`;
|
|
|
13764
14078
|
async listSessions(sinceISO) {
|
|
13765
14079
|
try {
|
|
13766
14080
|
const dbPath = this.globalStorageDbPath();
|
|
13767
|
-
if (!
|
|
14081
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
13768
14082
|
return null;
|
|
13769
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:%'`);
|
|
13770
14084
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
@@ -13893,7 +14207,7 @@ ${hint}`;
|
|
|
13893
14207
|
});
|
|
13894
14208
|
|
|
13895
14209
|
// src/agents/codex.ts
|
|
13896
|
-
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";
|
|
13897
14211
|
import { basename as basename3, join as join31 } from "path";
|
|
13898
14212
|
import { homedir as homedir13 } from "os";
|
|
13899
14213
|
import { parse, stringify } from "smol-toml";
|
|
@@ -13903,6 +14217,7 @@ function isRunworkManagedCodexKey(key) {
|
|
|
13903
14217
|
var CodexAdapter, CodexDesktopAdapter;
|
|
13904
14218
|
var init_codex = __esm(async () => {
|
|
13905
14219
|
init_types();
|
|
14220
|
+
init_transcript_sources();
|
|
13906
14221
|
init_session_digest();
|
|
13907
14222
|
init_session_listing();
|
|
13908
14223
|
init_skill_removal();
|
|
@@ -14121,6 +14436,12 @@ var init_codex = __esm(async () => {
|
|
|
14121
14436
|
return null;
|
|
14122
14437
|
}
|
|
14123
14438
|
}
|
|
14439
|
+
transcriptRoot() {
|
|
14440
|
+
return join31(homedir13(), ".codex", "sessions");
|
|
14441
|
+
}
|
|
14442
|
+
transcriptSources() {
|
|
14443
|
+
return [{ path: this.transcriptRoot(), kind: "dir" }];
|
|
14444
|
+
}
|
|
14124
14445
|
scanRolloutActivity(sinceMs) {
|
|
14125
14446
|
const days = new Set;
|
|
14126
14447
|
const result = {
|
|
@@ -14131,7 +14452,7 @@ var init_codex = __esm(async () => {
|
|
|
14131
14452
|
latestMs: 0,
|
|
14132
14453
|
activeDays: []
|
|
14133
14454
|
};
|
|
14134
|
-
const sessionsDir =
|
|
14455
|
+
const sessionsDir = this.transcriptRoot();
|
|
14135
14456
|
if (!existsSync37(sessionsDir))
|
|
14136
14457
|
return result;
|
|
14137
14458
|
const files = [];
|
|
@@ -14154,7 +14475,7 @@ var init_codex = __esm(async () => {
|
|
|
14154
14475
|
for (const file of files) {
|
|
14155
14476
|
let stat;
|
|
14156
14477
|
try {
|
|
14157
|
-
stat =
|
|
14478
|
+
stat = statSync7(file);
|
|
14158
14479
|
} catch {
|
|
14159
14480
|
continue;
|
|
14160
14481
|
}
|
|
@@ -14221,8 +14542,8 @@ var init_codex = __esm(async () => {
|
|
|
14221
14542
|
}
|
|
14222
14543
|
async readSessionDigests(sinceISO) {
|
|
14223
14544
|
try {
|
|
14224
|
-
const sessionsDir =
|
|
14225
|
-
if (!
|
|
14545
|
+
const sessionsDir = this.transcriptRoot();
|
|
14546
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
14226
14547
|
return null;
|
|
14227
14548
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
14228
14549
|
const files = [];
|
|
@@ -14246,7 +14567,7 @@ var init_codex = __esm(async () => {
|
|
|
14246
14567
|
for (const file of files) {
|
|
14247
14568
|
let stat;
|
|
14248
14569
|
try {
|
|
14249
|
-
stat =
|
|
14570
|
+
stat = statSync7(file);
|
|
14250
14571
|
} catch {
|
|
14251
14572
|
continue;
|
|
14252
14573
|
}
|
|
@@ -14269,8 +14590,8 @@ var init_codex = __esm(async () => {
|
|
|
14269
14590
|
}
|
|
14270
14591
|
async listSessions(sinceISO) {
|
|
14271
14592
|
try {
|
|
14272
|
-
const sessionsDir =
|
|
14273
|
-
if (!
|
|
14593
|
+
const sessionsDir = this.transcriptRoot();
|
|
14594
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
14274
14595
|
return null;
|
|
14275
14596
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
14276
14597
|
const files = [];
|
|
@@ -14294,7 +14615,7 @@ var init_codex = __esm(async () => {
|
|
|
14294
14615
|
for (const file of files) {
|
|
14295
14616
|
let stat;
|
|
14296
14617
|
try {
|
|
14297
|
-
stat =
|
|
14618
|
+
stat = statSync7(file);
|
|
14298
14619
|
} catch {
|
|
14299
14620
|
continue;
|
|
14300
14621
|
}
|
|
@@ -14333,7 +14654,7 @@ var init_codex = __esm(async () => {
|
|
|
14333
14654
|
}
|
|
14334
14655
|
async readSkillUsage(lastSyncAt) {
|
|
14335
14656
|
try {
|
|
14336
|
-
const sessionsDir =
|
|
14657
|
+
const sessionsDir = this.transcriptRoot();
|
|
14337
14658
|
if (!existsSync37(sessionsDir))
|
|
14338
14659
|
return null;
|
|
14339
14660
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -14350,7 +14671,7 @@ var init_codex = __esm(async () => {
|
|
|
14350
14671
|
if (entry.endsWith(".jsonl")) {
|
|
14351
14672
|
let fileStat;
|
|
14352
14673
|
try {
|
|
14353
|
-
fileStat =
|
|
14674
|
+
fileStat = statSync7(fullPath);
|
|
14354
14675
|
} catch {
|
|
14355
14676
|
continue;
|
|
14356
14677
|
}
|
|
@@ -14359,7 +14680,7 @@ var init_codex = __esm(async () => {
|
|
|
14359
14680
|
this.parseRolloutForSkills(fullPath, sinceMs, skillCounts);
|
|
14360
14681
|
} else {
|
|
14361
14682
|
try {
|
|
14362
|
-
if (
|
|
14683
|
+
if (statSync7(fullPath).isDirectory())
|
|
14363
14684
|
walkDir2(fullPath);
|
|
14364
14685
|
} catch {
|
|
14365
14686
|
continue;
|
|
@@ -14480,6 +14801,12 @@ var init_codex = __esm(async () => {
|
|
|
14480
14801
|
async listSessions() {
|
|
14481
14802
|
return null;
|
|
14482
14803
|
}
|
|
14804
|
+
async readSessionDigests() {
|
|
14805
|
+
return null;
|
|
14806
|
+
}
|
|
14807
|
+
transcriptSources() {
|
|
14808
|
+
return [];
|
|
14809
|
+
}
|
|
14483
14810
|
};
|
|
14484
14811
|
});
|
|
14485
14812
|
|
|
@@ -14602,7 +14929,7 @@ var init_cline = __esm(() => {
|
|
|
14602
14929
|
});
|
|
14603
14930
|
|
|
14604
14931
|
// src/agents/gemini.ts
|
|
14605
|
-
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";
|
|
14606
14933
|
import { basename as basename4, join as join33 } from "path";
|
|
14607
14934
|
import { homedir as homedir15 } from "os";
|
|
14608
14935
|
var GeminiAdapter;
|
|
@@ -14612,6 +14939,7 @@ var init_gemini = __esm(() => {
|
|
|
14612
14939
|
init_skill_removal();
|
|
14613
14940
|
init_trash();
|
|
14614
14941
|
init_types();
|
|
14942
|
+
init_transcript_sources();
|
|
14615
14943
|
init_instruction_hint();
|
|
14616
14944
|
init_json_config();
|
|
14617
14945
|
init_detection();
|
|
@@ -14684,7 +15012,7 @@ var init_gemini = __esm(() => {
|
|
|
14684
15012
|
}
|
|
14685
15013
|
async readUsageStats(lastSyncAt) {
|
|
14686
15014
|
try {
|
|
14687
|
-
const tmpDir =
|
|
15015
|
+
const tmpDir = this.transcriptRoot();
|
|
14688
15016
|
if (!existsSync39(tmpDir))
|
|
14689
15017
|
return null;
|
|
14690
15018
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -14714,7 +15042,7 @@ var init_gemini = __esm(() => {
|
|
|
14714
15042
|
const filePath = join33(chatsDir, file.name);
|
|
14715
15043
|
let stat;
|
|
14716
15044
|
try {
|
|
14717
|
-
stat =
|
|
15045
|
+
stat = statSync8(filePath);
|
|
14718
15046
|
} catch {
|
|
14719
15047
|
continue;
|
|
14720
15048
|
}
|
|
@@ -14762,8 +15090,14 @@ var init_gemini = __esm(() => {
|
|
|
14762
15090
|
return null;
|
|
14763
15091
|
}
|
|
14764
15092
|
}
|
|
15093
|
+
transcriptRoot() {
|
|
15094
|
+
return join33(homedir15(), ".gemini", "tmp");
|
|
15095
|
+
}
|
|
15096
|
+
transcriptSources() {
|
|
15097
|
+
return [{ path: this.transcriptRoot(), kind: "dir" }];
|
|
15098
|
+
}
|
|
14765
15099
|
*chatFiles(sinceMs) {
|
|
14766
|
-
const tmpDir =
|
|
15100
|
+
const tmpDir = this.transcriptRoot();
|
|
14767
15101
|
if (!existsSync39(tmpDir))
|
|
14768
15102
|
return;
|
|
14769
15103
|
let projects;
|
|
@@ -14788,7 +15122,7 @@ var init_gemini = __esm(() => {
|
|
|
14788
15122
|
const filePath = join33(chatsDir, file);
|
|
14789
15123
|
let stat;
|
|
14790
15124
|
try {
|
|
14791
|
-
stat =
|
|
15125
|
+
stat = statSync8(filePath);
|
|
14792
15126
|
} catch {
|
|
14793
15127
|
continue;
|
|
14794
15128
|
}
|
|
@@ -14800,6 +15134,8 @@ var init_gemini = __esm(() => {
|
|
|
14800
15134
|
}
|
|
14801
15135
|
async listSessions(sinceISO) {
|
|
14802
15136
|
try {
|
|
15137
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
15138
|
+
return null;
|
|
14803
15139
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
14804
15140
|
const sessions = [];
|
|
14805
15141
|
for (const { filePath, project, mtimeMs } of this.chatFiles(sinceMs)) {
|
|
@@ -14839,6 +15175,8 @@ var init_gemini = __esm(() => {
|
|
|
14839
15175
|
}
|
|
14840
15176
|
async readSessionDigests(sinceISO) {
|
|
14841
15177
|
try {
|
|
15178
|
+
if (!transcriptSourcesReadable(this.transcriptSources()))
|
|
15179
|
+
return null;
|
|
14842
15180
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
14843
15181
|
const digests = [];
|
|
14844
15182
|
for (const { filePath, project } of this.chatFiles(sinceMs)) {
|
|
@@ -14884,6 +15222,7 @@ var init_generic_adapter = __esm(() => {
|
|
|
14884
15222
|
init_json_config();
|
|
14885
15223
|
init_instruction_hint();
|
|
14886
15224
|
init_registry();
|
|
15225
|
+
init_detection_probes();
|
|
14887
15226
|
init_detection();
|
|
14888
15227
|
init_trash();
|
|
14889
15228
|
GenericAgentAdapter = class GenericAgentAdapter extends RegistryDetectedAdapter {
|
|
@@ -14908,7 +15247,13 @@ var init_generic_adapter = __esm(() => {
|
|
|
14908
15247
|
async writeMcpServers(servers, _scope) {
|
|
14909
15248
|
if (!this.def.mcpConfigPath)
|
|
14910
15249
|
return;
|
|
14911
|
-
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;
|
|
14912
15257
|
if (!filePath)
|
|
14913
15258
|
return;
|
|
14914
15259
|
const entries = {};
|
|
@@ -15061,6 +15406,26 @@ var init_detect = __esm(async () => {
|
|
|
15061
15406
|
ALL_ADAPTERS = buildAllAdapters();
|
|
15062
15407
|
});
|
|
15063
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
|
+
|
|
15064
15429
|
// src/utils/insight-id.ts
|
|
15065
15430
|
import { createHash as createHash3 } from "node:crypto";
|
|
15066
15431
|
function computeInsightId(userSeed, localKey) {
|
|
@@ -15211,7 +15576,7 @@ __export(exports_run_log, {
|
|
|
15211
15576
|
RUN_LOG_CAP: () => RUN_LOG_CAP,
|
|
15212
15577
|
ANALYST_ERROR_MAX_CHARS: () => ANALYST_ERROR_MAX_CHARS
|
|
15213
15578
|
});
|
|
15214
|
-
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";
|
|
15215
15580
|
import { dirname as dirname10, join as join37 } from "path";
|
|
15216
15581
|
import { homedir as homedir19 } from "os";
|
|
15217
15582
|
function runLogPath() {
|
|
@@ -15355,7 +15720,7 @@ function acquireRunLock(nowMs = Date.now()) {
|
|
|
15355
15720
|
return true;
|
|
15356
15721
|
} catch {
|
|
15357
15722
|
try {
|
|
15358
|
-
if (nowMs -
|
|
15723
|
+
if (nowMs - statSync9(p).mtimeMs < RUN_STALE_MS)
|
|
15359
15724
|
return false;
|
|
15360
15725
|
unlinkSync7(p);
|
|
15361
15726
|
} catch {
|
|
@@ -16043,6 +16408,7 @@ var init_reflect = __esm(async () => {
|
|
|
16043
16408
|
init_store();
|
|
16044
16409
|
init_client();
|
|
16045
16410
|
init_resolve();
|
|
16411
|
+
init_transcript_sources();
|
|
16046
16412
|
init_session_digest();
|
|
16047
16413
|
init_insight_id();
|
|
16048
16414
|
init_insight_store();
|
|
@@ -16107,13 +16473,13 @@ var init_reflect = __esm(async () => {
|
|
|
16107
16473
|
const seen = new Set;
|
|
16108
16474
|
for (const adapter2 of adapters) {
|
|
16109
16475
|
if (!adapter2.readSessionDigests) {
|
|
16110
|
-
perAgent.push({
|
|
16476
|
+
perAgent.push({ agentSlug: adapter2.slug, status: "no-reader", sessions: null });
|
|
16111
16477
|
continue;
|
|
16112
16478
|
}
|
|
16113
16479
|
try {
|
|
16114
16480
|
const sessions = await adapter2.readSessionDigests(sinceISO);
|
|
16115
16481
|
if (sessions === null) {
|
|
16116
|
-
perAgent.push(
|
|
16482
|
+
perAgent.push(diagnoseTranscriptRead(adapter2));
|
|
16117
16483
|
continue;
|
|
16118
16484
|
}
|
|
16119
16485
|
let added = 0;
|
|
@@ -16125,20 +16491,26 @@ var init_reflect = __esm(async () => {
|
|
|
16125
16491
|
digests.push(s);
|
|
16126
16492
|
added++;
|
|
16127
16493
|
}
|
|
16128
|
-
perAgent.push({
|
|
16129
|
-
} catch {
|
|
16130
|
-
perAgent.push(
|
|
16494
|
+
perAgent.push({ agentSlug: adapter2.slug, status: "ok", sessions: added });
|
|
16495
|
+
} catch (err) {
|
|
16496
|
+
perAgent.push(diagnoseTranscriptRead(adapter2, err));
|
|
16131
16497
|
}
|
|
16132
16498
|
}
|
|
16133
16499
|
if (!json) {
|
|
16134
16500
|
console.error(bold(`
|
|
16135
16501
|
Reflection over the last ${days} days`));
|
|
16136
|
-
for (const a of perAgent)
|
|
16137
|
-
|
|
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
|
+
}
|
|
16138
16510
|
}
|
|
16139
16511
|
if (digests.length === 0) {
|
|
16140
16512
|
if (json) {
|
|
16141
|
-
jsonOut({ insights: [], reason: "no-sessions" });
|
|
16513
|
+
jsonOut({ insights: [], reason: "no-sessions", perAgent });
|
|
16142
16514
|
return;
|
|
16143
16515
|
}
|
|
16144
16516
|
console.error(yellow(`
|
|
@@ -16503,19 +16875,19 @@ async function listLocalConversations(opts = {}) {
|
|
|
16503
16875
|
const perAgent = [];
|
|
16504
16876
|
for (const adapter2 of adapters) {
|
|
16505
16877
|
if (!adapter2.listSessions) {
|
|
16506
|
-
perAgent.push({
|
|
16878
|
+
perAgent.push({ agentSlug: adapter2.slug, status: "no-reader", sessions: null });
|
|
16507
16879
|
continue;
|
|
16508
16880
|
}
|
|
16509
16881
|
try {
|
|
16510
16882
|
const sessions = await adapter2.listSessions(sinceISO);
|
|
16511
16883
|
if (sessions === null) {
|
|
16512
|
-
perAgent.push(
|
|
16884
|
+
perAgent.push(diagnoseTranscriptRead(adapter2));
|
|
16513
16885
|
continue;
|
|
16514
16886
|
}
|
|
16515
16887
|
listings.push(sessions);
|
|
16516
|
-
perAgent.push({
|
|
16517
|
-
} catch {
|
|
16518
|
-
perAgent.push(
|
|
16888
|
+
perAgent.push({ agentSlug: adapter2.slug, status: "ok", sessions: sessions.length });
|
|
16889
|
+
} catch (err) {
|
|
16890
|
+
perAgent.push(diagnoseTranscriptRead(adapter2, err));
|
|
16519
16891
|
}
|
|
16520
16892
|
}
|
|
16521
16893
|
const conversations = mergeSessionListings(listings, {
|
|
@@ -16526,6 +16898,7 @@ async function listLocalConversations(opts = {}) {
|
|
|
16526
16898
|
}
|
|
16527
16899
|
var DEFAULT_IDLE_MINUTES = 10;
|
|
16528
16900
|
var init_conversation_registry = __esm(async () => {
|
|
16901
|
+
init_transcript_sources();
|
|
16529
16902
|
await init_detect();
|
|
16530
16903
|
});
|
|
16531
16904
|
|
|
@@ -17922,7 +18295,7 @@ var init_welcome = __esm(() => {
|
|
|
17922
18295
|
});
|
|
17923
18296
|
|
|
17924
18297
|
// src/index.ts
|
|
17925
|
-
import { Command as
|
|
18298
|
+
import { Command as Command40 } from "commander";
|
|
17926
18299
|
|
|
17927
18300
|
// src/commands/login.ts
|
|
17928
18301
|
init_login_flow();
|
|
@@ -18231,7 +18604,18 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
18231
18604
|
console.log("Syncing...");
|
|
18232
18605
|
try {
|
|
18233
18606
|
commitWorkingTree(cwd, `deploy: ${new Date().toISOString().replace("T", " ").slice(0, 19)}`);
|
|
18234
|
-
} 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
|
+
}
|
|
18235
18619
|
if (!hasCommits(cwd)) {
|
|
18236
18620
|
if (useJson) {
|
|
18237
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"]));
|
|
@@ -20372,9 +20756,14 @@ conversationsCommand.command("list").description("List local conversations acros
|
|
|
20372
20756
|
Local conversations, last ${days} day(s)` : `
|
|
20373
20757
|
Local conversations (all)`));
|
|
20374
20758
|
for (const a of result.perAgent) {
|
|
20375
|
-
if (a.
|
|
20759
|
+
if (a.status === "no-reader")
|
|
20376
20760
|
continue;
|
|
20377
|
-
|
|
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
|
+
}
|
|
20378
20767
|
}
|
|
20379
20768
|
console.log("");
|
|
20380
20769
|
const width = process.stdout.columns || 120;
|
|
@@ -20391,6 +20780,26 @@ Local conversations (all)`));
|
|
|
20391
20780
|
if (result.conversations.length === 0)
|
|
20392
20781
|
console.log(dim(" none"));
|
|
20393
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
|
+
});
|
|
20394
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) => {
|
|
20395
20804
|
const json = command.optsWithGlobals().json === true || !process.stdout.isTTY;
|
|
20396
20805
|
const days = parseDays(opts.days);
|
|
@@ -20454,11 +20863,12 @@ conversationsCommand.command("scan").description("Detect finished conversations
|
|
|
20454
20863
|
const snapshot = {
|
|
20455
20864
|
generatedAt: new Date().toISOString(),
|
|
20456
20865
|
idleMinutes,
|
|
20457
|
-
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
|
|
20458
20868
|
};
|
|
20459
20869
|
writeJsonAtomic(join43(homedir24(), ".runwork", "conversations.json"), snapshot);
|
|
20460
20870
|
if (json) {
|
|
20461
|
-
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 });
|
|
20462
20872
|
return;
|
|
20463
20873
|
}
|
|
20464
20874
|
console.log(green(`Scanned ${result.conversations.length} conversation(s): ${scan.queued} newly queued, ${scan.reopened} reopened, ${pending} pending analysis.`));
|
|
@@ -23433,7 +23843,11 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
23433
23843
|
if (!action.remoteContent)
|
|
23434
23844
|
continue;
|
|
23435
23845
|
const skillFile = makeSkillFile(action.name, action.remoteContent);
|
|
23436
|
-
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
|
+
}
|
|
23437
23851
|
newHashes[action.name] = {
|
|
23438
23852
|
localHash: contentHash(buildSkillMd2(skillFile)),
|
|
23439
23853
|
remoteHash: contentHash(action.remoteContent),
|
|
@@ -23475,7 +23889,11 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
23475
23889
|
vlog(` Pushed (conflict resolved): ${action.name}`);
|
|
23476
23890
|
} else if (resolution === "remote" && action.remoteContent) {
|
|
23477
23891
|
const skillFile = makeSkillFile(action.name, action.remoteContent);
|
|
23478
|
-
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
|
+
}
|
|
23479
23897
|
newHashes[action.name] = {
|
|
23480
23898
|
localHash: contentHash(buildSkillMd2(skillFile)),
|
|
23481
23899
|
remoteHash: contentHash(action.remoteContent),
|
|
@@ -23487,6 +23905,7 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
23487
23905
|
}
|
|
23488
23906
|
for (const _action of plan.skips) {}
|
|
23489
23907
|
const deletionSlugs = plan.deletions.map((action) => toSlug(action.name));
|
|
23908
|
+
let removalsFailed = false;
|
|
23490
23909
|
if (deletionSlugs.length) {
|
|
23491
23910
|
for (const adapter2 of ctx.adapters) {
|
|
23492
23911
|
if (!adapter2.supportsSkills() || !adapter2.removeSkills)
|
|
@@ -23494,14 +23913,19 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
23494
23913
|
for (const scope of ctx.scopes) {
|
|
23495
23914
|
try {
|
|
23496
23915
|
await adapter2.removeSkills(deletionSlugs, scope);
|
|
23497
|
-
} catch {
|
|
23916
|
+
} catch {
|
|
23917
|
+
removalsFailed = true;
|
|
23918
|
+
}
|
|
23498
23919
|
}
|
|
23499
23920
|
}
|
|
23500
23921
|
for (const action of plan.deletions) {
|
|
23501
|
-
vlog(` Deleted locally: ${action.name}`);
|
|
23922
|
+
vlog(removalsFailed ? ` Deletion of ${action.name} incomplete; will retry next sync` : ` Deleted locally: ${action.name}`);
|
|
23502
23923
|
}
|
|
23503
23924
|
}
|
|
23504
|
-
return
|
|
23925
|
+
return {
|
|
23926
|
+
newHashes,
|
|
23927
|
+
failedDeletions: removalsFailed ? plan.deletions.map((action) => action.name) : []
|
|
23928
|
+
};
|
|
23505
23929
|
}
|
|
23506
23930
|
function makeSkillFile(name, content) {
|
|
23507
23931
|
return {
|
|
@@ -23512,17 +23936,25 @@ function makeSkillFile(name, content) {
|
|
|
23512
23936
|
};
|
|
23513
23937
|
}
|
|
23514
23938
|
async function writeSkillToAgents(skillFile, source, ctx) {
|
|
23939
|
+
let attempted = 0;
|
|
23940
|
+
let succeeded = 0;
|
|
23515
23941
|
for (const adapter2 of ctx.adapters) {
|
|
23516
23942
|
if (source === "app" && ctx.hasMcp && adapter2.mcpProvidesSkills)
|
|
23517
23943
|
continue;
|
|
23518
23944
|
for (const scope of ctx.scopes) {
|
|
23519
23945
|
if (!adapter2.supportsSkills())
|
|
23520
23946
|
continue;
|
|
23947
|
+
attempted++;
|
|
23521
23948
|
try {
|
|
23522
23949
|
await adapter2.writeSkills([skillFile], scope);
|
|
23950
|
+
succeeded++;
|
|
23523
23951
|
} catch {}
|
|
23524
23952
|
}
|
|
23525
23953
|
}
|
|
23954
|
+
return { attempted, succeeded };
|
|
23955
|
+
}
|
|
23956
|
+
function writeLanded(outcome) {
|
|
23957
|
+
return outcome.attempted === 0 || outcome.succeeded > 0;
|
|
23526
23958
|
}
|
|
23527
23959
|
function extractDescription(content) {
|
|
23528
23960
|
const match = content.match(/^---\n[\s\S]*?description:\s*(.+)\n[\s\S]*?---/);
|
|
@@ -23937,7 +24369,7 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
23937
24369
|
if (adapters.length > 0) {
|
|
23938
24370
|
console.log(` Syncing to: ${adapters.map((a) => a.name).join(", ")}`);
|
|
23939
24371
|
}
|
|
23940
|
-
const newHashes = await executeSyncPlan(plan, resolvedConflicts, {
|
|
24372
|
+
const { newHashes, failedDeletions } = await executeSyncPlan(plan, resolvedConflicts, {
|
|
23941
24373
|
client,
|
|
23942
24374
|
workspaceId: state.workspaceId,
|
|
23943
24375
|
adapters,
|
|
@@ -24324,7 +24756,10 @@ This account is not a member of "${state.workspaceName || state.workspaceId}".`)
|
|
|
24324
24756
|
for (const [name, hash] of Object.entries(newHashes)) {
|
|
24325
24757
|
mergedHashes[name] = hash;
|
|
24326
24758
|
}
|
|
24759
|
+
const retryDeletions = new Set(failedDeletions);
|
|
24327
24760
|
for (const del of plan.deletions) {
|
|
24761
|
+
if (retryDeletions.has(del.name))
|
|
24762
|
+
continue;
|
|
24328
24763
|
delete mergedHashes[del.name];
|
|
24329
24764
|
}
|
|
24330
24765
|
state.skillHashes = mergedHashes;
|
|
@@ -25879,6 +26314,7 @@ async function checkAgentSetup() {
|
|
|
25879
26314
|
details.push(`${state.configuredAgents.length} agent(s) configured`);
|
|
25880
26315
|
}
|
|
25881
26316
|
let mcpChecked = false;
|
|
26317
|
+
let mcpAllHealthy = true;
|
|
25882
26318
|
for (const slug of state.configuredAgents) {
|
|
25883
26319
|
const adapter2 = getAdapterBySlug(slug);
|
|
25884
26320
|
if (!adapter2 || !adapter2.supportsMcpScope("user"))
|
|
@@ -25891,19 +26327,22 @@ async function checkAgentSetup() {
|
|
|
25891
26327
|
if (missingMcp.length > 0) {
|
|
25892
26328
|
details.push(`${missingMcp.length} MCP server(s) missing from ${slug} config`);
|
|
25893
26329
|
upgrade("warn");
|
|
25894
|
-
|
|
25895
|
-
details.push(`${state.mcpServers.length} MCP server(s) configured`);
|
|
26330
|
+
mcpAllHealthy = false;
|
|
25896
26331
|
}
|
|
25897
26332
|
mcpChecked = true;
|
|
25898
|
-
break;
|
|
25899
26333
|
} catch {}
|
|
25900
26334
|
}
|
|
25901
26335
|
}
|
|
26336
|
+
if (mcpChecked && mcpAllHealthy && state.mcpServers.length > 0) {
|
|
26337
|
+
details.push(`${state.mcpServers.length} MCP server(s) configured`);
|
|
26338
|
+
}
|
|
25902
26339
|
if (!mcpChecked && state.mcpServers.length > 0) {
|
|
25903
26340
|
details.push("could not verify MCP servers");
|
|
25904
26341
|
upgrade("warn");
|
|
25905
26342
|
}
|
|
25906
26343
|
let skillsChecked = false;
|
|
26344
|
+
let skillsAllHealthy = true;
|
|
26345
|
+
let skillsSummaryLine = null;
|
|
25907
26346
|
for (const slug of state.configuredAgents) {
|
|
25908
26347
|
const skillsDir = getSkillsDir(slug, "user");
|
|
25909
26348
|
if (!skillsDir)
|
|
@@ -25920,13 +26359,16 @@ async function checkAgentSetup() {
|
|
|
25920
26359
|
if (missingSkills.length > 0) {
|
|
25921
26360
|
details.push(`${missingSkills.length} skill(s) missing from ${slug}`);
|
|
25922
26361
|
upgrade("warn");
|
|
25923
|
-
|
|
26362
|
+
skillsAllHealthy = false;
|
|
26363
|
+
} else if (state.skills.length > 0 && !skillsSummaryLine) {
|
|
25924
26364
|
const mcpCoveredCount = state.skills.filter(isCoveredByMcp).length;
|
|
25925
26365
|
const onDiskCount = state.skills.length - mcpCoveredCount;
|
|
25926
|
-
|
|
26366
|
+
skillsSummaryLine = mcpCoveredCount > 0 ? `${state.skills.length} skill(s) installed (${onDiskCount} on disk, ${mcpCoveredCount} via MCP)` : `${state.skills.length} skill(s) installed`;
|
|
25927
26367
|
}
|
|
25928
26368
|
skillsChecked = true;
|
|
25929
|
-
|
|
26369
|
+
}
|
|
26370
|
+
if (skillsChecked && skillsAllHealthy && skillsSummaryLine) {
|
|
26371
|
+
details.push(skillsSummaryLine);
|
|
25930
26372
|
}
|
|
25931
26373
|
if (!skillsChecked && state.skills.length > 0) {
|
|
25932
26374
|
details.push("could not verify skills");
|
|
@@ -26101,7 +26543,7 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
26101
26543
|
}
|
|
26102
26544
|
|
|
26103
26545
|
// src/agents/runtime-detection.ts
|
|
26104
|
-
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";
|
|
26105
26547
|
import { homedir as homedir33 } from "os";
|
|
26106
26548
|
import { join as join55 } from "path";
|
|
26107
26549
|
var RUNWORK_SESSIONS_DIR = join55(homedir33(), ".runwork", "sessions");
|
|
@@ -26212,7 +26654,7 @@ function findCodexRolloutFile(threadId) {
|
|
|
26212
26654
|
const full = join55(dir, entry);
|
|
26213
26655
|
let s;
|
|
26214
26656
|
try {
|
|
26215
|
-
s =
|
|
26657
|
+
s = statSync11(full);
|
|
26216
26658
|
} catch {
|
|
26217
26659
|
continue;
|
|
26218
26660
|
}
|
|
@@ -26249,7 +26691,7 @@ function findNewestClaudeCodeSession() {
|
|
|
26249
26691
|
continue;
|
|
26250
26692
|
const full = join55(projectPath, file);
|
|
26251
26693
|
try {
|
|
26252
|
-
const s =
|
|
26694
|
+
const s = statSync11(full);
|
|
26253
26695
|
if (!best || s.mtimeMs > best.mtime) {
|
|
26254
26696
|
best = {
|
|
26255
26697
|
sessionId: file.replace(/\.jsonl$/, ""),
|
|
@@ -26282,7 +26724,7 @@ function findNewestCodexRollout() {
|
|
|
26282
26724
|
const full = join55(dir, entry);
|
|
26283
26725
|
let s;
|
|
26284
26726
|
try {
|
|
26285
|
-
s =
|
|
26727
|
+
s = statSync11(full);
|
|
26286
26728
|
} catch {
|
|
26287
26729
|
continue;
|
|
26288
26730
|
}
|
|
@@ -26506,13 +26948,627 @@ var doctorCommand = new Command34("doctor").description("Check system health: au
|
|
|
26506
26948
|
}
|
|
26507
26949
|
});
|
|
26508
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
|
+
|
|
26509
27565
|
// src/commands/share-convo.ts
|
|
26510
27566
|
init_store();
|
|
26511
27567
|
init_client();
|
|
26512
27568
|
init_resolve();
|
|
26513
|
-
import { Command as
|
|
26514
|
-
import { readFileSync as
|
|
26515
|
-
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";
|
|
26516
27572
|
import { tmpdir as tmpdir4 } from "os";
|
|
26517
27573
|
import { createHash as createHash6 } from "crypto";
|
|
26518
27574
|
|
|
@@ -26634,13 +27690,13 @@ function resolveLocalSessionShare(opts, conversation) {
|
|
|
26634
27690
|
process.exit(1);
|
|
26635
27691
|
}
|
|
26636
27692
|
const title = opts.title ?? conversation.title ?? conversation.project;
|
|
26637
|
-
const markdown = renderTranscriptMarkdown(
|
|
27693
|
+
const markdown = renderTranscriptMarkdown(readFileSync49(conversation.transcriptPath, "utf8"), family, title);
|
|
26638
27694
|
if (!markdown) {
|
|
26639
27695
|
console.error("Error: this conversation has no shareable content.");
|
|
26640
27696
|
process.exit(1);
|
|
26641
27697
|
}
|
|
26642
|
-
const tempDir = mkdtempSync4(
|
|
26643
|
-
const transcriptFile =
|
|
27698
|
+
const tempDir = mkdtempSync4(join59(tmpdir4(), "runwork-share-"));
|
|
27699
|
+
const transcriptFile = join59(tempDir, "transcript.md");
|
|
26644
27700
|
writeFileSync33(transcriptFile, markdown);
|
|
26645
27701
|
opts.transcriptFile = transcriptFile;
|
|
26646
27702
|
opts.nativeFile = opts.nativeFile ?? conversation.transcriptPath;
|
|
@@ -26657,7 +27713,7 @@ function nativeBundleFormatForAgent(slug) {
|
|
|
26657
27713
|
return "codex-rollout";
|
|
26658
27714
|
return null;
|
|
26659
27715
|
}
|
|
26660
|
-
function
|
|
27716
|
+
function sha256Hex2(content) {
|
|
26661
27717
|
return createHash6("sha256").update(content, "utf8").digest("hex");
|
|
26662
27718
|
}
|
|
26663
27719
|
function utf8ByteLength(content) {
|
|
@@ -26699,13 +27755,13 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26699
27755
|
const credentials = requireAuth();
|
|
26700
27756
|
const client = new ApiClient(credentials);
|
|
26701
27757
|
const { workspaceId } = await resolveWorkspace2(client, { workspace: opts.workspace });
|
|
26702
|
-
const transcriptContent =
|
|
27758
|
+
const transcriptContent = readFileSync49(opts.transcriptFile, "utf8");
|
|
26703
27759
|
const bundles = [
|
|
26704
27760
|
{
|
|
26705
27761
|
format: "transcript",
|
|
26706
27762
|
content: transcriptContent,
|
|
26707
27763
|
sizeBytes: utf8ByteLength(transcriptContent),
|
|
26708
|
-
sha256:
|
|
27764
|
+
sha256: sha256Hex2(transcriptContent)
|
|
26709
27765
|
}
|
|
26710
27766
|
];
|
|
26711
27767
|
const detected = detectCurrentAgent();
|
|
@@ -26724,12 +27780,12 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26724
27780
|
const nativeFormat = nativeBundleFormatForAgent(sourceAgent);
|
|
26725
27781
|
if (nativeFormat) {
|
|
26726
27782
|
try {
|
|
26727
|
-
const content =
|
|
27783
|
+
const content = readFileSync49(nativeFilePath, "utf8");
|
|
26728
27784
|
bundles.push({
|
|
26729
27785
|
format: nativeFormat,
|
|
26730
27786
|
content,
|
|
26731
27787
|
sizeBytes: utf8ByteLength(content),
|
|
26732
|
-
sha256:
|
|
27788
|
+
sha256: sha256Hex2(content)
|
|
26733
27789
|
});
|
|
26734
27790
|
} catch (err) {
|
|
26735
27791
|
console.error(`Warning: could not read native file ${nativeFilePath}: ${err instanceof Error ? err.message : err}`);
|
|
@@ -26740,7 +27796,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
26740
27796
|
let metadata = {};
|
|
26741
27797
|
if (opts.metadataFile) {
|
|
26742
27798
|
try {
|
|
26743
|
-
metadata = JSON.parse(
|
|
27799
|
+
metadata = JSON.parse(readFileSync49(opts.metadataFile, "utf8"));
|
|
26744
27800
|
} catch (err) {
|
|
26745
27801
|
console.error(`Error: --metadata-file is not valid JSON: ${err instanceof Error ? err.message : err}`);
|
|
26746
27802
|
process.exit(1);
|
|
@@ -26795,18 +27851,18 @@ Skipped: ${result.skipped.map((s) => `${s.identifier} (${s.reason})`).join(", ")
|
|
|
26795
27851
|
process.exit(1);
|
|
26796
27852
|
}
|
|
26797
27853
|
}
|
|
26798
|
-
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));
|
|
26799
27855
|
|
|
26800
27856
|
// src/commands/save-convo.ts
|
|
26801
|
-
import { Command as
|
|
26802
|
-
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));
|
|
26803
27859
|
|
|
26804
27860
|
// src/commands/inbox.ts
|
|
26805
27861
|
init_store();
|
|
26806
27862
|
init_client();
|
|
26807
27863
|
init_resolve();
|
|
26808
|
-
import { Command as
|
|
26809
|
-
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) => {
|
|
26810
27866
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
26811
27867
|
const scope = opts.filter === "received" || opts.filter === "sent" || opts.filter === "saved" ? opts.filter : "all";
|
|
26812
27868
|
const limit = opts.limit ? parseInt(opts.limit, 10) : 50;
|
|
@@ -26848,10 +27904,10 @@ init_store();
|
|
|
26848
27904
|
init_client();
|
|
26849
27905
|
init_resolve();
|
|
26850
27906
|
init_registry_data();
|
|
26851
|
-
import { Command as
|
|
26852
|
-
import { writeFileSync as writeFileSync34, mkdirSync as
|
|
26853
|
-
import { homedir as
|
|
26854
|
-
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";
|
|
26855
27911
|
import { spawn as spawn5 } from "child_process";
|
|
26856
27912
|
init_registry();
|
|
26857
27913
|
init_which();
|
|
@@ -26890,9 +27946,9 @@ function extractCodexUuid(rolloutContent) {
|
|
|
26890
27946
|
}
|
|
26891
27947
|
function placeClaudeJsonl(uuid, content, recipientCwd) {
|
|
26892
27948
|
const encoded = encodeClaudeCodeCwd(recipientCwd);
|
|
26893
|
-
const projectDir =
|
|
26894
|
-
|
|
26895
|
-
const placedAt =
|
|
27949
|
+
const projectDir = join60(homedir36(), ".claude", "projects", encoded);
|
|
27950
|
+
mkdirSync30(projectDir, { recursive: true });
|
|
27951
|
+
const placedAt = join60(projectDir, `${uuid}.jsonl`);
|
|
26896
27952
|
writeFileSync34(placedAt, content);
|
|
26897
27953
|
return { placedAt, runFromCwd: recipientCwd };
|
|
26898
27954
|
}
|
|
@@ -26901,10 +27957,10 @@ function placeCodexRollout(uuid, content) {
|
|
|
26901
27957
|
const yyyy = String(now.getUTCFullYear());
|
|
26902
27958
|
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
26903
27959
|
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
26904
|
-
const dir =
|
|
26905
|
-
|
|
27960
|
+
const dir = join60(homedir36(), ".codex", "sessions", yyyy, mm, dd);
|
|
27961
|
+
mkdirSync30(dir, { recursive: true });
|
|
26906
27962
|
const ts = now.toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
26907
|
-
const placedAt =
|
|
27963
|
+
const placedAt = join60(dir, `rollout-${ts}-${uuid}.jsonl`);
|
|
26908
27964
|
writeFileSync34(placedAt, content);
|
|
26909
27965
|
return { placedAt };
|
|
26910
27966
|
}
|
|
@@ -26924,7 +27980,15 @@ function isAgentInstalled(agent) {
|
|
|
26924
27980
|
}
|
|
26925
27981
|
return false;
|
|
26926
27982
|
}
|
|
26927
|
-
|
|
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) => {
|
|
26928
27992
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
26929
27993
|
const credentials = requireAuth();
|
|
26930
27994
|
const client = new ApiClient(credentials);
|
|
@@ -27018,12 +28082,8 @@ Bundle placed at: ${placement.placedAt}`);
|
|
|
27018
28082
|
return;
|
|
27019
28083
|
}
|
|
27020
28084
|
if (cap2.mode === "cli-resume") {
|
|
27021
|
-
|
|
27022
|
-
const
|
|
27023
|
-
const cliName = getAgent(target.slug)?.launch?.cli;
|
|
27024
|
-
if (resolvedCli && cliName && resolvedCli !== cliName && cmd.startsWith(`${cliName} `)) {
|
|
27025
|
-
cmd = `"${resolvedCli}" ${cmd.slice(cliName.length + 1)}`;
|
|
27026
|
-
}
|
|
28085
|
+
const { argv, display } = buildResumeCommand(cap2.cliResumeCommand ?? "", nativeUuid, resolveAgentCliCommand(target.slug), getAgent(target.slug)?.launch?.cli);
|
|
28086
|
+
const cmd = display;
|
|
27027
28087
|
const detected = detectCurrentAgent();
|
|
27028
28088
|
const insideSameAgent = detected && detected.slug === target.slug;
|
|
27029
28089
|
const shouldPrintOnly = opts.dryRun || insideSameAgent;
|
|
@@ -27055,10 +28115,11 @@ Or ask the assistant to continue the conversation in THIS session by ` + `fetchi
|
|
|
27055
28115
|
}
|
|
27056
28116
|
return;
|
|
27057
28117
|
}
|
|
27058
|
-
const
|
|
27059
|
-
const child = spawn5(
|
|
28118
|
+
const spec = toSpawnSpec(argv[0], argv.slice(1));
|
|
28119
|
+
const child = spawn5(spec.command, spec.args, {
|
|
27060
28120
|
cwd: placement.runFromCwd ?? recipientCwd,
|
|
27061
|
-
stdio: "inherit"
|
|
28121
|
+
stdio: "inherit",
|
|
28122
|
+
...spec.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
27062
28123
|
});
|
|
27063
28124
|
child.on("exit", (code) => {
|
|
27064
28125
|
process.exit(code ?? 0);
|
|
@@ -27154,7 +28215,7 @@ process.on("uncaughtException", (err) => {
|
|
|
27154
28215
|
console.error(`Uncaught exception: ${formatError(err)}`);
|
|
27155
28216
|
process.exit(1);
|
|
27156
28217
|
});
|
|
27157
|
-
var program = new
|
|
28218
|
+
var program = new Command40;
|
|
27158
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)");
|
|
27159
28220
|
program.addCommand(infoCommand);
|
|
27160
28221
|
program.addCommand(loginCommand);
|
|
@@ -27187,6 +28248,7 @@ program.addCommand(appsCommand);
|
|
|
27187
28248
|
program.addCommand(membersCommand);
|
|
27188
28249
|
program.addCommand(apiCommand);
|
|
27189
28250
|
program.addCommand(doctorCommand);
|
|
28251
|
+
program.addCommand(debugCommand);
|
|
27190
28252
|
program.addCommand(shareConvoCommand);
|
|
27191
28253
|
program.addCommand(saveConvoCommand);
|
|
27192
28254
|
program.addCommand(inboxCommand);
|