runtrim 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cli/runtrim.js +382 -33
- package/package.json +1 -1
package/dist-cli/runtrim.js
CHANGED
|
@@ -4165,13 +4165,274 @@ program.command("start").description("Guided RunTrim onboarding and daily loop")
|
|
|
4165
4165
|
console.log("");
|
|
4166
4166
|
}
|
|
4167
4167
|
});
|
|
4168
|
-
|
|
4168
|
+
var PROTOCOL_BLOCK_START = "<!-- RUNTRIM_PROTOCOL_START -->";
|
|
4169
|
+
var PROTOCOL_BLOCK_END = "<!-- RUNTRIM_PROTOCOL_END -->";
|
|
4170
|
+
var PROTOCOL_POINTER_BLOCK = `
|
|
4171
|
+
${PROTOCOL_BLOCK_START}
|
|
4172
|
+
This repo uses RunTrim as the guarded AI coding protocol.
|
|
4173
|
+
Before editing code, read RUNTRIM.md.
|
|
4174
|
+
Start every task with: runtrim go "<task>"
|
|
4175
|
+
Stay inside .runtrim/contracts/latest.md.
|
|
4176
|
+
After edits, ask the user to run: runtrim finish
|
|
4177
|
+
${PROTOCOL_BLOCK_END}
|
|
4178
|
+
`;
|
|
4179
|
+
function upsertProtocolBlock(filePath) {
|
|
4180
|
+
if (!fs10.existsSync(filePath)) return "skipped";
|
|
4181
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
4182
|
+
const startIdx = content.indexOf(PROTOCOL_BLOCK_START);
|
|
4183
|
+
const endIdx = content.indexOf(PROTOCOL_BLOCK_END);
|
|
4184
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
4185
|
+
const before = content.slice(0, startIdx);
|
|
4186
|
+
const after = content.slice(endIdx + PROTOCOL_BLOCK_END.length);
|
|
4187
|
+
const newContent = before + PROTOCOL_POINTER_BLOCK.trimStart() + after.replace(/^\n/, "");
|
|
4188
|
+
if (newContent === content) return "unchanged";
|
|
4189
|
+
fs10.writeFileSync(filePath, newContent, "utf-8");
|
|
4190
|
+
return "updated";
|
|
4191
|
+
}
|
|
4192
|
+
fs10.writeFileSync(filePath, content.trimEnd() + "\n" + PROTOCOL_POINTER_BLOCK, "utf-8");
|
|
4193
|
+
return "updated";
|
|
4194
|
+
}
|
|
4195
|
+
function createMinimalAgentPointerFile(filePath, filename) {
|
|
4196
|
+
const label = filename === "CLAUDE.md" ? "Claude Code" : "AI agents";
|
|
4197
|
+
const content = [
|
|
4198
|
+
`# ${label} Instructions`,
|
|
4199
|
+
"",
|
|
4200
|
+
"This repo uses RunTrim as the guarded AI coding protocol.",
|
|
4201
|
+
"Read RUNTRIM.md before editing any code.",
|
|
4202
|
+
"",
|
|
4203
|
+
PROTOCOL_POINTER_BLOCK.trim(),
|
|
4204
|
+
""
|
|
4205
|
+
].join("\n");
|
|
4206
|
+
fs10.writeFileSync(filePath, content, "utf-8");
|
|
4207
|
+
}
|
|
4208
|
+
function installProtocol(cwd, baseline, opts = {}) {
|
|
4209
|
+
var _a2, _b, _c, _d, _e, _f, _g;
|
|
4210
|
+
const configDir = getConfigDir(cwd);
|
|
4211
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4212
|
+
const extraFolders = [
|
|
4213
|
+
path10.join(configDir, "contracts"),
|
|
4214
|
+
path10.join(configDir, "memory"),
|
|
4215
|
+
path10.join(configDir, "bridge"),
|
|
4216
|
+
path10.join(configDir, "reports")
|
|
4217
|
+
];
|
|
4218
|
+
const createdFolders = [];
|
|
4219
|
+
for (const dir of extraFolders) {
|
|
4220
|
+
if (!fs10.existsSync(dir)) {
|
|
4221
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
4222
|
+
createdFolders.push(dir.replace(cwd + path10.sep, "").replace(/\\/g, "/"));
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
const scripts = (_a2 = baseline.scripts) != null ? _a2 : {};
|
|
4226
|
+
const buildCmd = (_d = (_c = (_b = scripts["build"]) != null ? _b : scripts["build:web"]) != null ? _c : scripts["build:all"]) != null ? _d : null;
|
|
4227
|
+
const testCmd = (_f = (_e = scripts["test"]) != null ? _e : scripts["test:run"]) != null ? _f : null;
|
|
4228
|
+
const runtrimMdPath = path10.join(cwd, "RUNTRIM.md");
|
|
4229
|
+
const runtrimMdExists = fs10.existsSync(runtrimMdPath);
|
|
4230
|
+
const runtrimMd = [
|
|
4231
|
+
"# RunTrim Protocol",
|
|
4232
|
+
"",
|
|
4233
|
+
"This repo uses RunTrim as the guarded AI coding control layer.",
|
|
4234
|
+
"",
|
|
4235
|
+
"## Starting an AI coding task",
|
|
4236
|
+
"",
|
|
4237
|
+
"Before any agent touches code, run:",
|
|
4238
|
+
"",
|
|
4239
|
+
"```",
|
|
4240
|
+
'runtrim go "<describe the task>"',
|
|
4241
|
+
"```",
|
|
4242
|
+
"",
|
|
4243
|
+
"This creates a scoped contract, loads project memory, and generates the guarded prompt for your agent.",
|
|
4244
|
+
"",
|
|
4245
|
+
"## Using your agent",
|
|
4246
|
+
"",
|
|
4247
|
+
"Paste the guarded prompt into Claude Code, Codex, Cursor, or any other AI coding agent.",
|
|
4248
|
+
"The agent receives allowed scope, forbidden areas, stop rules, and verification requirements.",
|
|
4249
|
+
"",
|
|
4250
|
+
"## After edits",
|
|
4251
|
+
"",
|
|
4252
|
+
"Run:",
|
|
4253
|
+
"",
|
|
4254
|
+
"```",
|
|
4255
|
+
"runtrim finish",
|
|
4256
|
+
"```",
|
|
4257
|
+
"",
|
|
4258
|
+
"This checks changed files, detects drift, scores risk, and saves the run report.",
|
|
4259
|
+
"",
|
|
4260
|
+
"## If you are an AI coding agent",
|
|
4261
|
+
"",
|
|
4262
|
+
"1. Read `.runtrim/contracts/latest.md` before touching any file.",
|
|
4263
|
+
"2. Stay inside the allowed scope defined in the contract.",
|
|
4264
|
+
"3. Do not touch forbidden systems or unrelated files.",
|
|
4265
|
+
"4. Stop immediately if scope must expand beyond the contract.",
|
|
4266
|
+
"5. Do not read, write, or reference `.env` files or secrets.",
|
|
4267
|
+
"6. Do not refactor code outside the direct task.",
|
|
4268
|
+
"7. After editing, tell the user to run: `runtrim finish`",
|
|
4269
|
+
"",
|
|
4270
|
+
"## Active contract",
|
|
4271
|
+
"",
|
|
4272
|
+
"If `.runtrim/contracts/latest.md` exists, it contains the active task contract.",
|
|
4273
|
+
"Follow it exactly.",
|
|
4274
|
+
"",
|
|
4275
|
+
"---",
|
|
4276
|
+
`Generated by RunTrim. Updated: ${now}`
|
|
4277
|
+
].join("\n");
|
|
4278
|
+
fs10.writeFileSync(runtrimMdPath, runtrimMd, "utf-8");
|
|
4279
|
+
const projectJsonPath = path10.join(configDir, "project.json");
|
|
4280
|
+
const projectJsonExists = fs10.existsSync(projectJsonPath);
|
|
4281
|
+
const projectJson = {
|
|
4282
|
+
name: baseline.projectName,
|
|
4283
|
+
stack: baseline.detectedStack,
|
|
4284
|
+
packageManager: baseline.packageManager,
|
|
4285
|
+
buildCommand: buildCmd,
|
|
4286
|
+
testCommand: testCmd,
|
|
4287
|
+
detectedAt: now
|
|
4288
|
+
};
|
|
4289
|
+
fs10.writeFileSync(projectJsonPath, JSON.stringify(projectJson, null, 2), "utf-8");
|
|
4290
|
+
const policiesPath = path10.join(configDir, "policies.json");
|
|
4291
|
+
const policiesJsonExists = fs10.existsSync(policiesPath);
|
|
4292
|
+
const detectedSensitive = ((_g = baseline.riskSurfaces) != null ? _g : []).map((s) => s.type.toLowerCase());
|
|
4293
|
+
const policies = {
|
|
4294
|
+
version: 1,
|
|
4295
|
+
protected: [
|
|
4296
|
+
".env*",
|
|
4297
|
+
"secrets",
|
|
4298
|
+
"*.key",
|
|
4299
|
+
"*.pem",
|
|
4300
|
+
"auth/**",
|
|
4301
|
+
"middleware.ts",
|
|
4302
|
+
"prisma/schema.prisma",
|
|
4303
|
+
"prisma/migrations/**",
|
|
4304
|
+
"database/migrations/**",
|
|
4305
|
+
"stripe/**",
|
|
4306
|
+
"billing/**",
|
|
4307
|
+
"payment/**",
|
|
4308
|
+
"webhooks/**",
|
|
4309
|
+
"package-lock.json",
|
|
4310
|
+
"pnpm-lock.yaml",
|
|
4311
|
+
"yarn.lock",
|
|
4312
|
+
".next/**",
|
|
4313
|
+
"dist/**",
|
|
4314
|
+
"build/**",
|
|
4315
|
+
"node_modules/**"
|
|
4316
|
+
],
|
|
4317
|
+
sensitive: [
|
|
4318
|
+
"auth",
|
|
4319
|
+
"billing",
|
|
4320
|
+
"payment",
|
|
4321
|
+
"middleware",
|
|
4322
|
+
"database",
|
|
4323
|
+
"schema",
|
|
4324
|
+
"migrations",
|
|
4325
|
+
"env",
|
|
4326
|
+
"secrets",
|
|
4327
|
+
"webhooks",
|
|
4328
|
+
...detectedSensitive.filter((s) => !["auth", "billing", "payment", "middleware", "database", "env", "secrets", "webhooks"].includes(s))
|
|
4329
|
+
],
|
|
4330
|
+
note: "These areas require explicit task scope before any agent edits.",
|
|
4331
|
+
updatedAt: now
|
|
4332
|
+
};
|
|
4333
|
+
fs10.writeFileSync(policiesPath, JSON.stringify(policies, null, 2), "utf-8");
|
|
4334
|
+
const baselineMdPath = path10.join(configDir, "memory", "baseline.md");
|
|
4335
|
+
const baselineMdExists = fs10.existsSync(baselineMdPath);
|
|
4336
|
+
const protectedList = policies.protected.slice(0, 10).map((p) => `- ${p}`).join("\n");
|
|
4337
|
+
const stackLine = baseline.detectedStack.join(", ") || "unknown";
|
|
4338
|
+
const baselineMd = [
|
|
4339
|
+
"# RunTrim Memory \u2014 Baseline",
|
|
4340
|
+
"",
|
|
4341
|
+
`Project: ${baseline.projectName}`,
|
|
4342
|
+
`Stack: ${stackLine}`,
|
|
4343
|
+
`Package manager: ${baseline.packageManager}`,
|
|
4344
|
+
...buildCmd ? [`Build: ${buildCmd}`] : [],
|
|
4345
|
+
...testCmd ? [`Test: ${testCmd}`] : [],
|
|
4346
|
+
"",
|
|
4347
|
+
"## Protected areas",
|
|
4348
|
+
"",
|
|
4349
|
+
protectedList,
|
|
4350
|
+
"",
|
|
4351
|
+
"## Project rules",
|
|
4352
|
+
"",
|
|
4353
|
+
'- Start every AI task with: runtrim go "<task>"',
|
|
4354
|
+
"- Stay inside the scoped contract.",
|
|
4355
|
+
"- Run runtrim finish after agent edits.",
|
|
4356
|
+
"- No unrelated refactors during a task.",
|
|
4357
|
+
"- Never touch .env files.",
|
|
4358
|
+
"",
|
|
4359
|
+
"## Prior agent decisions",
|
|
4360
|
+
"",
|
|
4361
|
+
"No prior runs recorded. This is the baseline for this project.",
|
|
4362
|
+
"",
|
|
4363
|
+
"---",
|
|
4364
|
+
`Created by runtrim init. Updated: ${now}`
|
|
4365
|
+
].join("\n");
|
|
4366
|
+
fs10.writeFileSync(baselineMdPath, baselineMd, "utf-8");
|
|
4367
|
+
const agentResults = [];
|
|
4368
|
+
const agentTargets = ["CLAUDE.md", "AGENTS.md"];
|
|
4369
|
+
for (const filename of agentTargets) {
|
|
4370
|
+
const filePath = path10.join(cwd, filename);
|
|
4371
|
+
if (fs10.existsSync(filePath)) {
|
|
4372
|
+
const result = upsertProtocolBlock(filePath);
|
|
4373
|
+
if (result !== "skipped") agentResults.push({ file: filename, result });
|
|
4374
|
+
} else if (opts.agentFiles) {
|
|
4375
|
+
createMinimalAgentPointerFile(filePath, filename);
|
|
4376
|
+
agentResults.push({ file: filename, result: "created" });
|
|
4377
|
+
} else {
|
|
4378
|
+
agentResults.push({ file: filename, result: "skipped" });
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4381
|
+
let cursorResult = "skipped";
|
|
4382
|
+
const cursorDir = path10.join(cwd, ".cursor");
|
|
4383
|
+
const cursorExists = fs10.existsSync(cursorDir);
|
|
4384
|
+
if (opts.cursor || cursorExists) {
|
|
4385
|
+
const rulesDir = path10.join(cursorDir, "rules");
|
|
4386
|
+
const mdcPath = path10.join(rulesDir, "runtrim.mdc");
|
|
4387
|
+
if (!fs10.existsSync(rulesDir)) fs10.mkdirSync(rulesDir, { recursive: true });
|
|
4388
|
+
const existed = fs10.existsSync(mdcPath);
|
|
4389
|
+
const cursorMdc = [
|
|
4390
|
+
"---",
|
|
4391
|
+
"description: RunTrim guarded AI coding protocol",
|
|
4392
|
+
"alwaysApply: true",
|
|
4393
|
+
"---",
|
|
4394
|
+
"",
|
|
4395
|
+
"# RunTrim Protocol",
|
|
4396
|
+
"",
|
|
4397
|
+
"This repo uses RunTrim as the guarded AI coding control layer.",
|
|
4398
|
+
"",
|
|
4399
|
+
"## Before editing any code",
|
|
4400
|
+
"",
|
|
4401
|
+
"1. Read `RUNTRIM.md` in the repo root.",
|
|
4402
|
+
"2. If `.runtrim/contracts/latest.md` exists, read the active contract.",
|
|
4403
|
+
"",
|
|
4404
|
+
"## Rules",
|
|
4405
|
+
"",
|
|
4406
|
+
"- Stay inside the allowed scope defined in the contract.",
|
|
4407
|
+
"- Do not touch forbidden files or unrelated systems.",
|
|
4408
|
+
"- Stop immediately if scope must expand.",
|
|
4409
|
+
"- Do not read or write `.env` files.",
|
|
4410
|
+
"- Do not refactor outside the task scope.",
|
|
4411
|
+
"",
|
|
4412
|
+
"## After editing",
|
|
4413
|
+
"",
|
|
4414
|
+
"Tell the user to run: `runtrim finish`"
|
|
4415
|
+
].join("\n");
|
|
4416
|
+
fs10.writeFileSync(mdcPath, cursorMdc, "utf-8");
|
|
4417
|
+
cursorResult = existed ? "updated" : "created";
|
|
4418
|
+
}
|
|
4419
|
+
return {
|
|
4420
|
+
runtrimMd: runtrimMdExists ? "updated" : "created",
|
|
4421
|
+
projectJson: projectJsonExists ? "updated" : "created",
|
|
4422
|
+
policiesJson: policiesJsonExists ? "updated" : "created",
|
|
4423
|
+
baselineMd: baselineMdExists ? "updated" : "created",
|
|
4424
|
+
folders: createdFolders,
|
|
4425
|
+
agentFiles: agentResults,
|
|
4426
|
+
cursorRules: cursorResult
|
|
4427
|
+
};
|
|
4428
|
+
}
|
|
4429
|
+
program.command("init").description("Install the RunTrim protocol in the current project").option("--refresh", "Refresh baseline audit, rules, and memory without overwriting config").option("--agent-files", "Create CLAUDE.md and AGENTS.md if missing, with RunTrim pointer").option("--cursor", "Create .cursor/rules/runtrim.mdc Cursor agent instructions").action(async (options) => {
|
|
4169
4430
|
var _a2;
|
|
4170
4431
|
const cwd = process.cwd();
|
|
4171
4432
|
const allowed = await ensureRepoAllowedForFree(cwd);
|
|
4172
4433
|
if (!allowed) return;
|
|
4173
4434
|
console.log("");
|
|
4174
|
-
console.log(
|
|
4435
|
+
console.log(GO_ACCENT.bold("RunTrim init"));
|
|
4175
4436
|
console.log("");
|
|
4176
4437
|
const initResult = await initializeRunTrim(cwd, {
|
|
4177
4438
|
refresh: options.refresh,
|
|
@@ -4179,33 +4440,46 @@ program.command("init").description("Initialize RunTrim in the current project")
|
|
|
4179
4440
|
});
|
|
4180
4441
|
if (!initResult.ok) return;
|
|
4181
4442
|
const baseline = (_a2 = loadProjectAudit(cwd)) != null ? _a2 : performBaselineProjectAudit(cwd, null);
|
|
4182
|
-
const
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
console.log(DIM("
|
|
4188
|
-
console.log(
|
|
4189
|
-
console.log(DIM("
|
|
4190
|
-
console.log("");
|
|
4191
|
-
console.log(DIM(" Scripts found"));
|
|
4192
|
-
console.log(DIM(" ") + chalk.white(scriptNames.length ? scriptNames.join(", ") : "none"));
|
|
4443
|
+
const protocol = installProtocol(cwd, baseline, {
|
|
4444
|
+
agentFiles: options.agentFiles,
|
|
4445
|
+
cursor: options.cursor
|
|
4446
|
+
});
|
|
4447
|
+
const stackLine = baseline.detectedStack.length ? baseline.detectedStack.join(" + ") : "unknown stack";
|
|
4448
|
+
console.log(DIM(" Project"));
|
|
4449
|
+
console.log(chalk.white(" " + baseline.projectName));
|
|
4450
|
+
console.log(DIM(" " + stackLine));
|
|
4193
4451
|
console.log("");
|
|
4194
|
-
console.log(DIM("
|
|
4195
|
-
|
|
4196
|
-
|
|
4452
|
+
console.log(DIM(" Protocol"));
|
|
4453
|
+
const protocolFiles = [
|
|
4454
|
+
["RUNTRIM.md", protocol.runtrimMd],
|
|
4455
|
+
[".runtrim/project.json", protocol.projectJson],
|
|
4456
|
+
[".runtrim/policies.json", protocol.policiesJson],
|
|
4457
|
+
[".runtrim/memory/baseline.md", protocol.baselineMd]
|
|
4458
|
+
];
|
|
4459
|
+
for (const [file, result] of protocolFiles) {
|
|
4460
|
+
console.log(DIM(" ") + chalk.white(file.padEnd(34)) + DIM(result));
|
|
4197
4461
|
}
|
|
4198
4462
|
console.log("");
|
|
4199
|
-
console.log(DIM(
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4463
|
+
console.log(DIM(" Agent pointers"));
|
|
4464
|
+
for (const { file, result } of protocol.agentFiles) {
|
|
4465
|
+
const color = result === "skipped" ? DIM : chalk.white;
|
|
4466
|
+
console.log(DIM(" ") + color(file.padEnd(34)) + DIM(result));
|
|
4467
|
+
}
|
|
4468
|
+
if (protocol.cursorRules !== "skipped") {
|
|
4469
|
+
console.log(DIM(" ") + chalk.white(".cursor/rules/runtrim.mdc".padEnd(34)) + DIM(protocol.cursorRules));
|
|
4470
|
+
} else if (options.cursor) {
|
|
4471
|
+
console.log(DIM(" Cursor rules skipped (.cursor/ not found and --cursor not passed)"));
|
|
4472
|
+
}
|
|
4206
4473
|
console.log("");
|
|
4474
|
+
if (protocol.folders.length > 0) {
|
|
4475
|
+
console.log(DIM(" Folders created"));
|
|
4476
|
+
for (const f of protocol.folders) {
|
|
4477
|
+
console.log(DIM(" " + f + "/"));
|
|
4478
|
+
}
|
|
4479
|
+
console.log("");
|
|
4480
|
+
}
|
|
4207
4481
|
console.log(DIM(" Next"));
|
|
4208
|
-
console.log(chalk.white(' runtrim
|
|
4482
|
+
console.log(chalk.white(' runtrim go "your first task"'));
|
|
4209
4483
|
console.log("");
|
|
4210
4484
|
});
|
|
4211
4485
|
var agentCommand = program.command("agent").description("Show or configure local agent execution settings");
|
|
@@ -4877,7 +5151,7 @@ program.command("prepare <task>").description("Prepare a guarded prompt without
|
|
|
4877
5151
|
}
|
|
4878
5152
|
);
|
|
4879
5153
|
program.command("go <task>").description("Bridge Mode: generate a scoped contract, write protocol files, and prepare the guarded prompt").option("--no-clipboard", "Print prompt to terminal instead of copying to clipboard").option("--no-sync", "Skip cloud sync even if a CLI token is configured").option("--no-bridge", "Skip bridge file writing (RUNTRIM.md, contracts, memory)").option("--print", "Always print the prompt to terminal in addition to copying").option("--monitor", "Open local panel monitor in the background (best effort)").action(async (task, options) => {
|
|
4880
|
-
var _a2, _b, _c, _d;
|
|
5154
|
+
var _a2, _b, _c, _d, _e, _f, _g;
|
|
4881
5155
|
const cwd = process.cwd();
|
|
4882
5156
|
const allowed = await ensureRepoAllowedForFree(cwd);
|
|
4883
5157
|
if (!allowed) return;
|
|
@@ -4886,6 +5160,38 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4886
5160
|
if (!initResult.ok) return;
|
|
4887
5161
|
}
|
|
4888
5162
|
const config = loadConfig(cwd);
|
|
5163
|
+
const globalAuth = loadGlobalAuth();
|
|
5164
|
+
const rawToken = (_b = (_a2 = globalAuth == null ? void 0 : globalAuth.token) != null ? _a2 : config.syncToken) != null ? _b : null;
|
|
5165
|
+
const apiBase = resolveApiBase(config);
|
|
5166
|
+
if (rawToken == null ? void 0 : rawToken.startsWith("rt_live_")) {
|
|
5167
|
+
let serverResult = null;
|
|
5168
|
+
try {
|
|
5169
|
+
const res = await fetch(`${apiBase}/api/cli/usage/bridge-run`, {
|
|
5170
|
+
method: "POST",
|
|
5171
|
+
headers: { Authorization: `Bearer ${rawToken}` }
|
|
5172
|
+
});
|
|
5173
|
+
if (res.ok) {
|
|
5174
|
+
const body = await res.json();
|
|
5175
|
+
serverResult = {
|
|
5176
|
+
allowed: body.allowed,
|
|
5177
|
+
plan: body.plan,
|
|
5178
|
+
used: (_c = body.usage.bridgeRunsUsed) != null ? _c : 0,
|
|
5179
|
+
limit: body.usage.bridgeRunsLimit
|
|
5180
|
+
};
|
|
5181
|
+
}
|
|
5182
|
+
} catch (e) {
|
|
5183
|
+
}
|
|
5184
|
+
if (serverResult !== null && !serverResult.allowed) {
|
|
5185
|
+
printBridgeLimitMessage(serverResult.used);
|
|
5186
|
+
return;
|
|
5187
|
+
}
|
|
5188
|
+
} else {
|
|
5189
|
+
const check = checkAndIncrementLocalUsage();
|
|
5190
|
+
if (!check.allowed) {
|
|
5191
|
+
printBridgeLimitMessage(check.used);
|
|
5192
|
+
return;
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
4889
5195
|
const auditSpinner = oraFactory({ text: " Auditing task...", color: "blue" }).start();
|
|
4890
5196
|
await new Promise((r) => setTimeout(r, 180));
|
|
4891
5197
|
const audit = auditTask(task, config, cwd);
|
|
@@ -4917,7 +5223,7 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4917
5223
|
}
|
|
4918
5224
|
const runs = loadAllRuns(cwd);
|
|
4919
5225
|
const projectAudit = loadProjectAudit(cwd);
|
|
4920
|
-
const projectName = (
|
|
5226
|
+
const projectName = (_d = projectAudit == null ? void 0 : projectAudit.projectName) != null ? _d : path10.basename(cwd);
|
|
4921
5227
|
const memoryMarkdown = (() => {
|
|
4922
5228
|
try {
|
|
4923
5229
|
return readMemory(cwd);
|
|
@@ -4954,9 +5260,9 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4954
5260
|
updateRun(run.id, { bridgeManagedFiles: bridgeManagedPaths }, cwd);
|
|
4955
5261
|
let synced = false;
|
|
4956
5262
|
if (options.sync !== false) {
|
|
4957
|
-
const
|
|
4958
|
-
const
|
|
4959
|
-
if (
|
|
5263
|
+
const globalAuth2 = loadGlobalAuth();
|
|
5264
|
+
const rawToken2 = (_f = (_e = globalAuth2 == null ? void 0 : globalAuth2.token) != null ? _e : config.syncToken) != null ? _f : null;
|
|
5265
|
+
if (rawToken2 == null ? void 0 : rawToken2.startsWith("rt_live_")) {
|
|
4960
5266
|
try {
|
|
4961
5267
|
const freshRuns = loadAllRuns(cwd);
|
|
4962
5268
|
const payload = buildSyncPayload({
|
|
@@ -4967,10 +5273,10 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4967
5273
|
memoryMarkdown: memoryMarkdown != null ? memoryMarkdown : "",
|
|
4968
5274
|
runs: freshRuns
|
|
4969
5275
|
});
|
|
4970
|
-
const
|
|
4971
|
-
const r = await fetch(`${
|
|
5276
|
+
const apiBase2 = resolveApiBase(config);
|
|
5277
|
+
const r = await fetch(`${apiBase2}/api/sync`, {
|
|
4972
5278
|
method: "POST",
|
|
4973
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${
|
|
5279
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${rawToken2}` },
|
|
4974
5280
|
body: JSON.stringify(payload)
|
|
4975
5281
|
});
|
|
4976
5282
|
synced = r.ok;
|
|
@@ -4978,7 +5284,7 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4978
5284
|
}
|
|
4979
5285
|
}
|
|
4980
5286
|
}
|
|
4981
|
-
const riskColor = (
|
|
5287
|
+
const riskColor = (_g = { low: chalk.green, medium: chalk.yellow, high: chalk.hex("#FF8C00"), critical: chalk.red }[bridgeCtx.riskLevel]) != null ? _g : chalk.white;
|
|
4982
5288
|
console.log("");
|
|
4983
5289
|
console.log(GO_ACCENT.bold("RunTrim go"));
|
|
4984
5290
|
console.log("");
|
|
@@ -5858,6 +6164,49 @@ var statusColors = {
|
|
|
5858
6164
|
drift_detected: chalk.red,
|
|
5859
6165
|
blocked: chalk.red
|
|
5860
6166
|
};
|
|
6167
|
+
var GLOBAL_USAGE_FILE = path10.join(os3.homedir(), ".runtrim", "usage.json");
|
|
6168
|
+
var FREE_BRIDGE_LIMIT_LOCAL = 5;
|
|
6169
|
+
function currentUsagePeriod() {
|
|
6170
|
+
const d = /* @__PURE__ */ new Date();
|
|
6171
|
+
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
6172
|
+
}
|
|
6173
|
+
function loadLocalUsageRuns() {
|
|
6174
|
+
var _a2;
|
|
6175
|
+
try {
|
|
6176
|
+
const raw = JSON.parse(fs10.readFileSync(GLOBAL_USAGE_FILE, "utf-8"));
|
|
6177
|
+
return (_a2 = raw.bridgeRuns) != null ? _a2 : {};
|
|
6178
|
+
} catch (e) {
|
|
6179
|
+
return {};
|
|
6180
|
+
}
|
|
6181
|
+
}
|
|
6182
|
+
function saveLocalUsageRuns(bridgeRuns) {
|
|
6183
|
+
const dir = path10.dirname(GLOBAL_USAGE_FILE);
|
|
6184
|
+
if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
|
|
6185
|
+
fs10.writeFileSync(GLOBAL_USAGE_FILE, JSON.stringify({ bridgeRuns }, null, 2), "utf-8");
|
|
6186
|
+
}
|
|
6187
|
+
function checkAndIncrementLocalUsage() {
|
|
6188
|
+
var _a2;
|
|
6189
|
+
const period = currentUsagePeriod();
|
|
6190
|
+
const runs = loadLocalUsageRuns();
|
|
6191
|
+
const used = (_a2 = runs[period]) != null ? _a2 : 0;
|
|
6192
|
+
if (used >= FREE_BRIDGE_LIMIT_LOCAL) {
|
|
6193
|
+
return { allowed: false, used };
|
|
6194
|
+
}
|
|
6195
|
+
runs[period] = used + 1;
|
|
6196
|
+
saveLocalUsageRuns(runs);
|
|
6197
|
+
return { allowed: true, used: used + 1 };
|
|
6198
|
+
}
|
|
6199
|
+
function printBridgeLimitMessage(used) {
|
|
6200
|
+
console.log("");
|
|
6201
|
+
console.log(chalk.red.bold(" Free Bridge limit reached."));
|
|
6202
|
+
console.log("");
|
|
6203
|
+
console.log(chalk.white(` You have used ${used} local guarded run${used === 1 ? "" : "s"} this month.`));
|
|
6204
|
+
console.log(DIM(" Upgrade to Pro for unlimited Bridge Mode, cloud sync, project memory,"));
|
|
6205
|
+
console.log(DIM(" reports, and continuation history."));
|
|
6206
|
+
console.log("");
|
|
6207
|
+
console.log(chalk.white(" https://www.runtrim.com/pricing"));
|
|
6208
|
+
console.log("");
|
|
6209
|
+
}
|
|
5861
6210
|
var GLOBAL_AUTH_DIR = path10.join(os3.homedir(), ".runtrim");
|
|
5862
6211
|
var GLOBAL_AUTH_FILE = path10.join(GLOBAL_AUTH_DIR, "auth.json");
|
|
5863
6212
|
function loadGlobalAuth() {
|