@remixhq/claude-plugin 0.1.23 → 0.1.25
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/.claude-plugin/plugin.json +1 -1
- package/dist/hook-post-collab.cjs +3 -3
- package/dist/hook-post-collab.cjs.map +1 -1
- package/dist/hook-stop-collab.cjs +261 -183
- package/dist/hook-stop-collab.cjs.map +1 -1
- package/dist/hook-user-prompt.cjs +310 -228
- package/dist/hook-user-prompt.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +209 -73
- package/dist/mcp-server.cjs.map +1 -1
- package/package.json +3 -3
- package/skills/submit-change-step/SKILL.md +1 -1
|
@@ -38,7 +38,7 @@ var require_windows = __commonJS({
|
|
|
38
38
|
module2.exports = isexe;
|
|
39
39
|
isexe.sync = sync;
|
|
40
40
|
var fs11 = require("fs");
|
|
41
|
-
function checkPathExt(
|
|
41
|
+
function checkPathExt(path18, options) {
|
|
42
42
|
var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
|
|
43
43
|
if (!pathext) {
|
|
44
44
|
return true;
|
|
@@ -49,25 +49,25 @@ var require_windows = __commonJS({
|
|
|
49
49
|
}
|
|
50
50
|
for (var i2 = 0; i2 < pathext.length; i2++) {
|
|
51
51
|
var p = pathext[i2].toLowerCase();
|
|
52
|
-
if (p &&
|
|
52
|
+
if (p && path18.substr(-p.length).toLowerCase() === p) {
|
|
53
53
|
return true;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
return false;
|
|
57
57
|
}
|
|
58
|
-
function checkStat(stat,
|
|
58
|
+
function checkStat(stat, path18, options) {
|
|
59
59
|
if (!stat.isSymbolicLink() && !stat.isFile()) {
|
|
60
60
|
return false;
|
|
61
61
|
}
|
|
62
|
-
return checkPathExt(
|
|
62
|
+
return checkPathExt(path18, options);
|
|
63
63
|
}
|
|
64
|
-
function isexe(
|
|
65
|
-
fs11.stat(
|
|
66
|
-
cb(er, er ? false : checkStat(stat,
|
|
64
|
+
function isexe(path18, options, cb) {
|
|
65
|
+
fs11.stat(path18, function(er, stat) {
|
|
66
|
+
cb(er, er ? false : checkStat(stat, path18, options));
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
function sync(
|
|
70
|
-
return checkStat(fs11.statSync(
|
|
69
|
+
function sync(path18, options) {
|
|
70
|
+
return checkStat(fs11.statSync(path18), path18, options);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
});
|
|
@@ -79,13 +79,13 @@ var require_mode = __commonJS({
|
|
|
79
79
|
module2.exports = isexe;
|
|
80
80
|
isexe.sync = sync;
|
|
81
81
|
var fs11 = require("fs");
|
|
82
|
-
function isexe(
|
|
83
|
-
fs11.stat(
|
|
82
|
+
function isexe(path18, options, cb) {
|
|
83
|
+
fs11.stat(path18, function(er, stat) {
|
|
84
84
|
cb(er, er ? false : checkStat(stat, options));
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
-
function sync(
|
|
88
|
-
return checkStat(fs11.statSync(
|
|
87
|
+
function sync(path18, options) {
|
|
88
|
+
return checkStat(fs11.statSync(path18), options);
|
|
89
89
|
}
|
|
90
90
|
function checkStat(stat, options) {
|
|
91
91
|
return stat.isFile() && checkMode(stat, options);
|
|
@@ -119,7 +119,7 @@ var require_isexe = __commonJS({
|
|
|
119
119
|
}
|
|
120
120
|
module2.exports = isexe;
|
|
121
121
|
isexe.sync = sync;
|
|
122
|
-
function isexe(
|
|
122
|
+
function isexe(path18, options, cb) {
|
|
123
123
|
if (typeof options === "function") {
|
|
124
124
|
cb = options;
|
|
125
125
|
options = {};
|
|
@@ -129,7 +129,7 @@ var require_isexe = __commonJS({
|
|
|
129
129
|
throw new TypeError("callback not provided");
|
|
130
130
|
}
|
|
131
131
|
return new Promise(function(resolve, reject) {
|
|
132
|
-
isexe(
|
|
132
|
+
isexe(path18, options || {}, function(er, is) {
|
|
133
133
|
if (er) {
|
|
134
134
|
reject(er);
|
|
135
135
|
} else {
|
|
@@ -138,7 +138,7 @@ var require_isexe = __commonJS({
|
|
|
138
138
|
});
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
|
-
core(
|
|
141
|
+
core(path18, options || {}, function(er, is) {
|
|
142
142
|
if (er) {
|
|
143
143
|
if (er.code === "EACCES" || options && options.ignoreErrors) {
|
|
144
144
|
er = null;
|
|
@@ -148,9 +148,9 @@ var require_isexe = __commonJS({
|
|
|
148
148
|
cb(er, is);
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
|
-
function sync(
|
|
151
|
+
function sync(path18, options) {
|
|
152
152
|
try {
|
|
153
|
-
return core.sync(
|
|
153
|
+
return core.sync(path18, options || {});
|
|
154
154
|
} catch (er) {
|
|
155
155
|
if (options && options.ignoreErrors || er.code === "EACCES") {
|
|
156
156
|
return false;
|
|
@@ -167,7 +167,7 @@ var require_which = __commonJS({
|
|
|
167
167
|
"node_modules/which/which.js"(exports2, module2) {
|
|
168
168
|
"use strict";
|
|
169
169
|
var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
|
|
170
|
-
var
|
|
170
|
+
var path18 = require("path");
|
|
171
171
|
var COLON = isWindows ? ";" : ":";
|
|
172
172
|
var isexe = require_isexe();
|
|
173
173
|
var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
|
|
@@ -205,7 +205,7 @@ var require_which = __commonJS({
|
|
|
205
205
|
return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
|
|
206
206
|
const ppRaw = pathEnv[i2];
|
|
207
207
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
208
|
-
const pCmd =
|
|
208
|
+
const pCmd = path18.join(pathPart, cmd);
|
|
209
209
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
210
210
|
resolve(subStep(p, i2, 0));
|
|
211
211
|
});
|
|
@@ -232,7 +232,7 @@ var require_which = __commonJS({
|
|
|
232
232
|
for (let i2 = 0; i2 < pathEnv.length; i2++) {
|
|
233
233
|
const ppRaw = pathEnv[i2];
|
|
234
234
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
235
|
-
const pCmd =
|
|
235
|
+
const pCmd = path18.join(pathPart, cmd);
|
|
236
236
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
237
237
|
for (let j = 0; j < pathExt.length; j++) {
|
|
238
238
|
const cur = p + pathExt[j];
|
|
@@ -280,7 +280,7 @@ var require_path_key = __commonJS({
|
|
|
280
280
|
var require_resolveCommand = __commonJS({
|
|
281
281
|
"node_modules/cross-spawn/lib/util/resolveCommand.js"(exports2, module2) {
|
|
282
282
|
"use strict";
|
|
283
|
-
var
|
|
283
|
+
var path18 = require("path");
|
|
284
284
|
var which = require_which();
|
|
285
285
|
var getPathKey = require_path_key();
|
|
286
286
|
function resolveCommandAttempt(parsed, withoutPathExt) {
|
|
@@ -298,7 +298,7 @@ var require_resolveCommand = __commonJS({
|
|
|
298
298
|
try {
|
|
299
299
|
resolved = which.sync(parsed.command, {
|
|
300
300
|
path: env[getPathKey({ env })],
|
|
301
|
-
pathExt: withoutPathExt ?
|
|
301
|
+
pathExt: withoutPathExt ? path18.delimiter : void 0
|
|
302
302
|
});
|
|
303
303
|
} catch (e) {
|
|
304
304
|
} finally {
|
|
@@ -307,7 +307,7 @@ var require_resolveCommand = __commonJS({
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
if (resolved) {
|
|
310
|
-
resolved =
|
|
310
|
+
resolved = path18.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
|
|
311
311
|
}
|
|
312
312
|
return resolved;
|
|
313
313
|
}
|
|
@@ -361,8 +361,8 @@ var require_shebang_command = __commonJS({
|
|
|
361
361
|
if (!match) {
|
|
362
362
|
return null;
|
|
363
363
|
}
|
|
364
|
-
const [
|
|
365
|
-
const binary =
|
|
364
|
+
const [path18, argument] = match[0].replace(/#! ?/, "").split(" ");
|
|
365
|
+
const binary = path18.split("/").pop();
|
|
366
366
|
if (binary === "env") {
|
|
367
367
|
return argument;
|
|
368
368
|
}
|
|
@@ -397,7 +397,7 @@ var require_readShebang = __commonJS({
|
|
|
397
397
|
var require_parse = __commonJS({
|
|
398
398
|
"node_modules/cross-spawn/lib/parse.js"(exports2, module2) {
|
|
399
399
|
"use strict";
|
|
400
|
-
var
|
|
400
|
+
var path18 = require("path");
|
|
401
401
|
var resolveCommand = require_resolveCommand();
|
|
402
402
|
var escape = require_escape();
|
|
403
403
|
var readShebang = require_readShebang();
|
|
@@ -422,7 +422,7 @@ var require_parse = __commonJS({
|
|
|
422
422
|
const needsShell = !isExecutableRegExp.test(commandFile);
|
|
423
423
|
if (parsed.options.forceShell || needsShell) {
|
|
424
424
|
const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
|
|
425
|
-
parsed.command =
|
|
425
|
+
parsed.command = path18.normalize(parsed.command);
|
|
426
426
|
parsed.command = escape.command(parsed.command);
|
|
427
427
|
parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
|
|
428
428
|
const shellCommand = [parsed.command].concat(parsed.args).join(" ");
|
|
@@ -540,7 +540,7 @@ __export(hook_user_prompt_exports, {
|
|
|
540
540
|
module.exports = __toCommonJS(hook_user_prompt_exports);
|
|
541
541
|
var import_node_child_process8 = require("child_process");
|
|
542
542
|
var import_node_fs7 = require("fs");
|
|
543
|
-
var
|
|
543
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
544
544
|
|
|
545
545
|
// node_modules/@remixhq/core/dist/chunk-7XJGOKEO.js
|
|
546
546
|
var RemixError = class extends Error {
|
|
@@ -4943,13 +4943,13 @@ var logOutputSync = ({ serializedResult, fdNumber, state, verboseInfo, encoding,
|
|
|
4943
4943
|
}
|
|
4944
4944
|
};
|
|
4945
4945
|
var writeToFiles = (serializedResult, stdioItems, outputFiles) => {
|
|
4946
|
-
for (const { path:
|
|
4947
|
-
const pathString = typeof
|
|
4946
|
+
for (const { path: path18, append } of stdioItems.filter(({ type }) => FILE_TYPES.has(type))) {
|
|
4947
|
+
const pathString = typeof path18 === "string" ? path18 : path18.toString();
|
|
4948
4948
|
if (append || outputFiles.has(pathString)) {
|
|
4949
|
-
(0, import_node_fs4.appendFileSync)(
|
|
4949
|
+
(0, import_node_fs4.appendFileSync)(path18, serializedResult);
|
|
4950
4950
|
} else {
|
|
4951
4951
|
outputFiles.add(pathString);
|
|
4952
|
-
(0, import_node_fs4.writeFileSync)(
|
|
4952
|
+
(0, import_node_fs4.writeFileSync)(path18, serializedResult);
|
|
4953
4953
|
}
|
|
4954
4954
|
}
|
|
4955
4955
|
};
|
|
@@ -7668,6 +7668,21 @@ async function writeCollabBinding(repoRoot, binding) {
|
|
|
7668
7668
|
return filePath;
|
|
7669
7669
|
}
|
|
7670
7670
|
|
|
7671
|
+
// src/collab-init-spawn-lock.ts
|
|
7672
|
+
var import_node_crypto = require("crypto");
|
|
7673
|
+
var import_node_os4 = __toESM(require("os"), 1);
|
|
7674
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
7675
|
+
function stateRoot() {
|
|
7676
|
+
const configured = process.env.REMIX_CLAUDE_PLUGIN_HOOK_STATE_ROOT?.trim();
|
|
7677
|
+
return configured || import_node_path6.default.join(import_node_os4.default.tmpdir(), "remix-claude-plugin-hooks");
|
|
7678
|
+
}
|
|
7679
|
+
function sha256Hex(value) {
|
|
7680
|
+
return (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
|
|
7681
|
+
}
|
|
7682
|
+
function collabInitSpawnLockPath(repoRoot) {
|
|
7683
|
+
return import_node_path6.default.join(stateRoot(), "collab-init-spawn-locks", `${sha256Hex(repoRoot)}.json`);
|
|
7684
|
+
}
|
|
7685
|
+
|
|
7671
7686
|
// src/history-routing.ts
|
|
7672
7687
|
var STRONG_MEMORY_FIRST_PATTERNS = [
|
|
7673
7688
|
/\bwhy\b/i,
|
|
@@ -7773,25 +7788,25 @@ function buildPromptRoutingAdvisory(intent) {
|
|
|
7773
7788
|
|
|
7774
7789
|
// src/deferred-turn-queue.ts
|
|
7775
7790
|
var import_promises14 = __toESM(require("fs/promises"), 1);
|
|
7776
|
-
var
|
|
7777
|
-
var
|
|
7791
|
+
var import_node_os5 = __toESM(require("os"), 1);
|
|
7792
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
7778
7793
|
var DEFERRED_TURN_SCHEMA_VERSION = 1;
|
|
7779
7794
|
var DEFERRED_TURN_MAX_ATTEMPTS = 10;
|
|
7780
7795
|
var DEFERRED_TURN_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
7781
7796
|
var DEFERRED_TURN_DIR = "deferred-turns";
|
|
7782
|
-
function
|
|
7797
|
+
function stateRoot2() {
|
|
7783
7798
|
const configured = process.env.REMIX_CLAUDE_PLUGIN_HOOK_STATE_ROOT?.trim();
|
|
7784
|
-
return configured ||
|
|
7799
|
+
return configured || import_node_path7.default.join(import_node_os5.default.tmpdir(), "remix-claude-plugin-hooks");
|
|
7785
7800
|
}
|
|
7786
7801
|
function getDeferredTurnDirPath() {
|
|
7787
|
-
return
|
|
7802
|
+
return import_node_path7.default.join(stateRoot2(), DEFERRED_TURN_DIR);
|
|
7788
7803
|
}
|
|
7789
7804
|
function deferredTurnFileName(sessionId, turnId) {
|
|
7790
7805
|
const safe = (s) => s.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
7791
7806
|
return `${safe(sessionId)}-${safe(turnId)}.json`;
|
|
7792
7807
|
}
|
|
7793
7808
|
function getDeferredTurnFilePath(sessionId, turnId) {
|
|
7794
|
-
return
|
|
7809
|
+
return import_node_path7.default.join(getDeferredTurnDirPath(), deferredTurnFileName(sessionId, turnId));
|
|
7795
7810
|
}
|
|
7796
7811
|
async function writeDeferredTurn(record) {
|
|
7797
7812
|
if (record.schemaVersion !== DEFERRED_TURN_SCHEMA_VERSION) {
|
|
@@ -7852,7 +7867,7 @@ async function listDeferredTurnsForRepo(repoRoot) {
|
|
|
7852
7867
|
const entries = [];
|
|
7853
7868
|
for (const entry of dirEntries) {
|
|
7854
7869
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
7855
|
-
const filePath =
|
|
7870
|
+
const filePath = import_node_path7.default.join(dir, entry.name);
|
|
7856
7871
|
const record = await readDeferredTurnFile(filePath);
|
|
7857
7872
|
if (!record) continue;
|
|
7858
7873
|
if (record.repoRoot !== repoRoot) continue;
|
|
@@ -7876,7 +7891,7 @@ async function pruneStaleDeferredTurns(maxAgeMs = DEFERRED_TURN_TTL_MS) {
|
|
|
7876
7891
|
const now = Date.now();
|
|
7877
7892
|
for (const entry of dirEntries) {
|
|
7878
7893
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
7879
|
-
const filePath =
|
|
7894
|
+
const filePath = import_node_path7.default.join(dir, entry.name);
|
|
7880
7895
|
const record = await readDeferredTurnFile(filePath);
|
|
7881
7896
|
if (!record) {
|
|
7882
7897
|
const stat = await import_promises14.default.stat(filePath).catch(() => null);
|
|
@@ -7912,8 +7927,8 @@ async function recordDeferredTurnFailedAttempt(filePath) {
|
|
|
7912
7927
|
|
|
7913
7928
|
// src/deferred-turn-drainer.ts
|
|
7914
7929
|
var import_promises23 = __toESM(require("fs/promises"), 1);
|
|
7915
|
-
var
|
|
7916
|
-
var
|
|
7930
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
7931
|
+
var import_node_crypto5 = require("crypto");
|
|
7917
7932
|
|
|
7918
7933
|
// node_modules/@remixhq/core/dist/collab.js
|
|
7919
7934
|
var import_promises15 = __toESM(require("fs/promises"), 1);
|
|
@@ -8015,7 +8030,7 @@ function buildBranchMismatchHint(params) {
|
|
|
8015
8030
|
`Switch to ${describeBranch(params.branchName)} or rerun with ${overrideFlag} if this is intentional.`
|
|
8016
8031
|
].join("\n");
|
|
8017
8032
|
}
|
|
8018
|
-
function
|
|
8033
|
+
function sha256Hex2(value) {
|
|
8019
8034
|
return (0, import_crypto.createHash)("sha256").update(value).digest("hex");
|
|
8020
8035
|
}
|
|
8021
8036
|
function getCollabStateRoot() {
|
|
@@ -8029,7 +8044,7 @@ function buildLaneStateKey(params) {
|
|
|
8029
8044
|
const stableSource = repoRoot || "unknown-repo-root";
|
|
8030
8045
|
const fingerprintSource = fingerprint || "unknown-repo-fingerprint";
|
|
8031
8046
|
const laneSource = laneId || "unknown-lane";
|
|
8032
|
-
return
|
|
8047
|
+
return sha256Hex2(`${stableSource}::${fingerprintSource}::${laneSource}`);
|
|
8033
8048
|
}
|
|
8034
8049
|
function getSnapshotsRoot() {
|
|
8035
8050
|
return import_path3.default.join(getCollabStateRoot(), "snapshots");
|
|
@@ -8230,7 +8245,7 @@ async function writeLocalBaseline(baseline) {
|
|
|
8230
8245
|
await writeJsonAtomic(getBaselinePath(baseline), normalized);
|
|
8231
8246
|
return normalized;
|
|
8232
8247
|
}
|
|
8233
|
-
function
|
|
8248
|
+
function sha256Hex22(value) {
|
|
8234
8249
|
return (0, import_crypto2.createHash)("sha256").update(value).digest("hex");
|
|
8235
8250
|
}
|
|
8236
8251
|
function getSnapshotRecordPath(snapshotId) {
|
|
@@ -8281,7 +8296,7 @@ async function persistBlob(blobHash, content) {
|
|
|
8281
8296
|
}
|
|
8282
8297
|
function buildSnapshotHash(files) {
|
|
8283
8298
|
const manifest = files.map((file) => `${file.path} ${file.mode} ${file.blobHash} ${file.size}`).join("\n");
|
|
8284
|
-
return
|
|
8299
|
+
return sha256Hex22(manifest);
|
|
8285
8300
|
}
|
|
8286
8301
|
async function inspectLocalSnapshot(params) {
|
|
8287
8302
|
const repoRoot = params.repoRoot;
|
|
@@ -8292,7 +8307,7 @@ async function inspectLocalSnapshot(params) {
|
|
|
8292
8307
|
const stat = await import_promises17.default.lstat(absolutePath);
|
|
8293
8308
|
if (stat.isSymbolicLink()) {
|
|
8294
8309
|
const linkTarget = await import_promises17.default.readlink(absolutePath);
|
|
8295
|
-
const blobHash2 =
|
|
8310
|
+
const blobHash2 = sha256Hex22(`symlink:${linkTarget}`);
|
|
8296
8311
|
if (params.persistBlobs !== false) {
|
|
8297
8312
|
await persistBlob(blobHash2, linkTarget);
|
|
8298
8313
|
}
|
|
@@ -8305,7 +8320,7 @@ async function inspectLocalSnapshot(params) {
|
|
|
8305
8320
|
continue;
|
|
8306
8321
|
}
|
|
8307
8322
|
const content = await import_promises17.default.readFile(absolutePath);
|
|
8308
|
-
const blobHash =
|
|
8323
|
+
const blobHash = sha256Hex22(content);
|
|
8309
8324
|
if (params.persistBlobs !== false) {
|
|
8310
8325
|
await persistBlob(blobHash, content);
|
|
8311
8326
|
}
|
|
@@ -8409,7 +8424,7 @@ async function diffLocalSnapshots(params) {
|
|
|
8409
8424
|
baseSnapshotId: params.baseSnapshotId,
|
|
8410
8425
|
targetSnapshotId: params.targetSnapshotId,
|
|
8411
8426
|
diff,
|
|
8412
|
-
diffSha256: diff ?
|
|
8427
|
+
diffSha256: diff ? sha256Hex22(diff) : null,
|
|
8413
8428
|
changedPaths,
|
|
8414
8429
|
stats: summarizeUnifiedDiff(diff)
|
|
8415
8430
|
};
|
|
@@ -8827,6 +8842,15 @@ function shouldRequireRemoteLaneForCurrentBranch(params) {
|
|
|
8827
8842
|
if (params.currentBranch === defaultBranch) return false;
|
|
8828
8843
|
return !params.binding.laneId || params.binding.currentAppId === params.binding.upstreamAppId;
|
|
8829
8844
|
}
|
|
8845
|
+
function resolveLaneLookupProjectId(params) {
|
|
8846
|
+
const currentBranch = normalizeBranchName2(params.currentBranch);
|
|
8847
|
+
const defaultBranch = normalizeBranchName2(params.defaultBranch);
|
|
8848
|
+
const localProjectId = params.localBinding.projectId ?? null;
|
|
8849
|
+
if (currentBranch && currentBranch !== defaultBranch && localProjectId) {
|
|
8850
|
+
return localProjectId;
|
|
8851
|
+
}
|
|
8852
|
+
return params.explicitRootProjectId ?? (params.requireRemoteLane ? void 0 : localProjectId ?? params.fallbackProjectId ?? void 0);
|
|
8853
|
+
}
|
|
8830
8854
|
async function persistResolvedLane(repoRoot, binding) {
|
|
8831
8855
|
await writeCollabBinding(repoRoot, {
|
|
8832
8856
|
projectId: binding.projectId,
|
|
@@ -8905,7 +8929,14 @@ async function resolveActiveLaneBindingUncached(params, state) {
|
|
|
8905
8929
|
};
|
|
8906
8930
|
}
|
|
8907
8931
|
const laneResp2 = await params.api.resolveProjectLaneBinding({
|
|
8908
|
-
projectId:
|
|
8932
|
+
projectId: resolveLaneLookupProjectId({
|
|
8933
|
+
explicitRootProjectId: state.explicitRootBinding?.projectId,
|
|
8934
|
+
localBinding,
|
|
8935
|
+
currentBranch,
|
|
8936
|
+
defaultBranch: state.defaultBranch,
|
|
8937
|
+
requireRemoteLane,
|
|
8938
|
+
fallbackProjectId: state.projectId
|
|
8939
|
+
}),
|
|
8909
8940
|
repoFingerprint: state.repoFingerprint ?? void 0,
|
|
8910
8941
|
remoteUrl: state.remoteUrl ?? void 0,
|
|
8911
8942
|
defaultBranch: state.defaultBranch ?? void 0,
|
|
@@ -9756,6 +9787,59 @@ function buildWorkspaceMetadata(params) {
|
|
|
9756
9787
|
}
|
|
9757
9788
|
return metadata;
|
|
9758
9789
|
}
|
|
9790
|
+
async function findExistingChangeStepByIdempotency(params) {
|
|
9791
|
+
const idempotencyKey = params.idempotencyKey?.trim();
|
|
9792
|
+
if (!idempotencyKey) return null;
|
|
9793
|
+
const resp = await params.api.listChangeSteps(params.appId, { limit: 1, idempotencyKey });
|
|
9794
|
+
const responseObject = unwrapResponseObject(
|
|
9795
|
+
resp,
|
|
9796
|
+
"change step list"
|
|
9797
|
+
);
|
|
9798
|
+
const steps = Array.isArray(responseObject) ? responseObject : Array.isArray(responseObject.items) ? responseObject.items : [];
|
|
9799
|
+
return steps.find((step) => step.idempotencyKey === idempotencyKey) ?? null;
|
|
9800
|
+
}
|
|
9801
|
+
async function writeBaselineFromSucceededChangeStep(params) {
|
|
9802
|
+
const nextServerHeadHash = typeof params.changeStep.headCommitHash === "string" ? params.changeStep.headCommitHash.trim() : "";
|
|
9803
|
+
if (!nextServerHeadHash) {
|
|
9804
|
+
throw buildFinalizeCliError({
|
|
9805
|
+
message: "Backend returned a succeeded change step without a head commit hash.",
|
|
9806
|
+
exitCode: 1,
|
|
9807
|
+
hint: "This is a backend invariant violation; retry will not help. Run `remix collab status` before trying again.",
|
|
9808
|
+
disposition: "terminal",
|
|
9809
|
+
reason: "missing_head_commit_hash"
|
|
9810
|
+
});
|
|
9811
|
+
}
|
|
9812
|
+
let nextServerRevisionId = typeof params.changeStep.resultRevisionId === "string" ? params.changeStep.resultRevisionId.trim() : "";
|
|
9813
|
+
let nextServerTreeHash = null;
|
|
9814
|
+
if (!nextServerRevisionId) {
|
|
9815
|
+
const freshHeadResp = await params.api.getAppHead(params.job.currentAppId);
|
|
9816
|
+
const freshHead = unwrapResponseObject(freshHeadResp, "app head");
|
|
9817
|
+
if (freshHead.headCommitHash !== nextServerHeadHash || !freshHead.headRevisionId) {
|
|
9818
|
+
throw buildFinalizeCliError({
|
|
9819
|
+
message: "Backend returned a succeeded change step without a matching result revision.",
|
|
9820
|
+
exitCode: 1,
|
|
9821
|
+
hint: "The local baseline was not advanced because the post-step revision could not be verified. Restart the backend/CLI and retry after checking `remix collab status`.",
|
|
9822
|
+
disposition: "terminal",
|
|
9823
|
+
reason: "missing_result_revision_id"
|
|
9824
|
+
});
|
|
9825
|
+
}
|
|
9826
|
+
nextServerRevisionId = freshHead.headRevisionId;
|
|
9827
|
+
nextServerTreeHash = freshHead.treeHash ?? null;
|
|
9828
|
+
}
|
|
9829
|
+
await writeLocalBaseline({
|
|
9830
|
+
repoRoot: params.job.repoRoot,
|
|
9831
|
+
repoFingerprint: params.job.repoFingerprint,
|
|
9832
|
+
laneId: params.job.laneId,
|
|
9833
|
+
currentAppId: params.job.currentAppId,
|
|
9834
|
+
branchName: params.job.branchName,
|
|
9835
|
+
lastSnapshotId: params.snapshot.id,
|
|
9836
|
+
lastSnapshotHash: params.snapshot.snapshotHash,
|
|
9837
|
+
lastServerRevisionId: nextServerRevisionId,
|
|
9838
|
+
lastServerTreeHash: nextServerTreeHash,
|
|
9839
|
+
lastServerHeadHash: nextServerHeadHash,
|
|
9840
|
+
lastSeenLocalCommitHash: params.snapshot.localCommitHash
|
|
9841
|
+
});
|
|
9842
|
+
}
|
|
9759
9843
|
async function harvestPreTurnEvents(repoRoot, fromCommit, toCommit) {
|
|
9760
9844
|
if (!toCommit) return null;
|
|
9761
9845
|
try {
|
|
@@ -9941,6 +10025,34 @@ async function processClaimedPendingFinalizeJobInner(params) {
|
|
|
9941
10025
|
});
|
|
9942
10026
|
}
|
|
9943
10027
|
const replayNeeded = appHead.headCommitHash !== submissionBaseHeadHash || baselineDrifted;
|
|
10028
|
+
if (replayNeeded) {
|
|
10029
|
+
const existingChangeStep = await findExistingChangeStepByIdempotency({
|
|
10030
|
+
api: params.api,
|
|
10031
|
+
appId: job.currentAppId,
|
|
10032
|
+
idempotencyKey: job.idempotencyKey
|
|
10033
|
+
});
|
|
10034
|
+
if (existingChangeStep) {
|
|
10035
|
+
const changeStep2 = existingChangeStep.status === "succeeded" ? existingChangeStep : await pollChangeStep(params.api, job.currentAppId, existingChangeStep.id);
|
|
10036
|
+
invalidateAppHeadCache(job.currentAppId);
|
|
10037
|
+
invalidateAppDeltaCacheForApp(job.currentAppId);
|
|
10038
|
+
await writeBaselineFromSucceededChangeStep({ api: params.api, job, snapshot, changeStep: changeStep2 });
|
|
10039
|
+
await updatePendingFinalizeJob(job.id, {
|
|
10040
|
+
status: "completed",
|
|
10041
|
+
metadata: { changeStepId: String(changeStep2.id ?? "") }
|
|
10042
|
+
});
|
|
10043
|
+
return {
|
|
10044
|
+
mode: "changed_turn",
|
|
10045
|
+
idempotencyKey: job.idempotencyKey ?? "",
|
|
10046
|
+
queued: false,
|
|
10047
|
+
jobId: job.id,
|
|
10048
|
+
repoState,
|
|
10049
|
+
changeStep: changeStep2,
|
|
10050
|
+
collabTurn: null,
|
|
10051
|
+
autoSync: null,
|
|
10052
|
+
warnings: []
|
|
10053
|
+
};
|
|
10054
|
+
}
|
|
10055
|
+
}
|
|
9944
10056
|
if (replayNeeded) {
|
|
9945
10057
|
try {
|
|
9946
10058
|
const replayResp = await params.api.startChangeStepReplay(job.currentAppId, {
|
|
@@ -10038,46 +10150,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
|
|
|
10038
10150
|
const changeStep = await pollChangeStep(params.api, job.currentAppId, String(createdStep.id));
|
|
10039
10151
|
invalidateAppHeadCache(job.currentAppId);
|
|
10040
10152
|
invalidateAppDeltaCacheForApp(job.currentAppId);
|
|
10041
|
-
|
|
10042
|
-
if (!nextServerHeadHash) {
|
|
10043
|
-
throw buildFinalizeCliError({
|
|
10044
|
-
message: "Backend returned a succeeded change step without a head commit hash.",
|
|
10045
|
-
exitCode: 1,
|
|
10046
|
-
hint: "This is a backend invariant violation; retry will not help. Run `remix collab status` before trying again.",
|
|
10047
|
-
disposition: "terminal",
|
|
10048
|
-
reason: "missing_head_commit_hash"
|
|
10049
|
-
});
|
|
10050
|
-
}
|
|
10051
|
-
let nextServerRevisionId = typeof changeStep.resultRevisionId === "string" ? changeStep.resultRevisionId.trim() : "";
|
|
10052
|
-
let nextServerTreeHash = null;
|
|
10053
|
-
if (!nextServerRevisionId) {
|
|
10054
|
-
const freshHeadResp = await params.api.getAppHead(job.currentAppId);
|
|
10055
|
-
const freshHead = unwrapResponseObject(freshHeadResp, "app head");
|
|
10056
|
-
if (freshHead.headCommitHash !== nextServerHeadHash || !freshHead.headRevisionId) {
|
|
10057
|
-
throw buildFinalizeCliError({
|
|
10058
|
-
message: "Backend returned a succeeded change step without a matching result revision.",
|
|
10059
|
-
exitCode: 1,
|
|
10060
|
-
hint: "The local baseline was not advanced because the post-step revision could not be verified. Restart the backend/CLI and retry after checking `remix collab status`.",
|
|
10061
|
-
disposition: "terminal",
|
|
10062
|
-
reason: "missing_result_revision_id"
|
|
10063
|
-
});
|
|
10064
|
-
}
|
|
10065
|
-
nextServerRevisionId = freshHead.headRevisionId;
|
|
10066
|
-
nextServerTreeHash = freshHead.treeHash ?? null;
|
|
10067
|
-
}
|
|
10068
|
-
await writeLocalBaseline({
|
|
10069
|
-
repoRoot: job.repoRoot,
|
|
10070
|
-
repoFingerprint: job.repoFingerprint,
|
|
10071
|
-
laneId: job.laneId,
|
|
10072
|
-
currentAppId: job.currentAppId,
|
|
10073
|
-
branchName: job.branchName,
|
|
10074
|
-
lastSnapshotId: snapshot.id,
|
|
10075
|
-
lastSnapshotHash: snapshot.snapshotHash,
|
|
10076
|
-
lastServerRevisionId: nextServerRevisionId,
|
|
10077
|
-
lastServerTreeHash: nextServerTreeHash,
|
|
10078
|
-
lastServerHeadHash: nextServerHeadHash,
|
|
10079
|
-
lastSeenLocalCommitHash: snapshot.localCommitHash
|
|
10080
|
-
});
|
|
10153
|
+
await writeBaselineFromSucceededChangeStep({ api: params.api, job, snapshot, changeStep });
|
|
10081
10154
|
await updatePendingFinalizeJob(job.id, {
|
|
10082
10155
|
status: "completed",
|
|
10083
10156
|
metadata: { changeStepId: String(changeStep.id ?? "") }
|
|
@@ -10362,15 +10435,17 @@ function isFinalizePreflightFailureCode(value) {
|
|
|
10362
10435
|
|
|
10363
10436
|
// src/auto-fix-dispatcher.ts
|
|
10364
10437
|
var import_node_child_process6 = require("child_process");
|
|
10438
|
+
var import_node_crypto4 = require("crypto");
|
|
10365
10439
|
var import_node_fs6 = require("fs");
|
|
10366
|
-
var
|
|
10440
|
+
var import_node_os8 = __toESM(require("os"), 1);
|
|
10441
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
10367
10442
|
|
|
10368
10443
|
// src/finalize-failure-marker.ts
|
|
10369
10444
|
var import_promises19 = __toESM(require("fs/promises"), 1);
|
|
10370
|
-
var
|
|
10371
|
-
var FINALIZE_FAILURE_MARKER_REL =
|
|
10445
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
10446
|
+
var FINALIZE_FAILURE_MARKER_REL = import_node_path8.default.join(".remix", ".last-finalize-failure.json");
|
|
10372
10447
|
function markerPath(repoRoot) {
|
|
10373
|
-
return
|
|
10448
|
+
return import_node_path8.default.join(repoRoot, FINALIZE_FAILURE_MARKER_REL);
|
|
10374
10449
|
}
|
|
10375
10450
|
async function readFinalizeFailureMarker(repoRoot) {
|
|
10376
10451
|
const raw = await import_promises19.default.readFile(markerPath(repoRoot), "utf8").catch(() => null);
|
|
@@ -10387,7 +10462,7 @@ async function readFinalizeFailureMarker(repoRoot) {
|
|
|
10387
10462
|
}
|
|
10388
10463
|
async function writeFinalizeFailureMarker(marker) {
|
|
10389
10464
|
const filePath = markerPath(marker.repoRoot);
|
|
10390
|
-
await import_promises19.default.mkdir(
|
|
10465
|
+
await import_promises19.default.mkdir(import_node_path8.default.dirname(filePath), { recursive: true });
|
|
10391
10466
|
const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
10392
10467
|
await import_promises19.default.writeFile(tmpPath, JSON.stringify(marker, null, 2), "utf8");
|
|
10393
10468
|
await import_promises19.default.rename(tmpPath, filePath);
|
|
@@ -10413,31 +10488,31 @@ function buildFreshFailureMarker(params) {
|
|
|
10413
10488
|
}
|
|
10414
10489
|
|
|
10415
10490
|
// src/hook-diagnostics.ts
|
|
10416
|
-
var
|
|
10491
|
+
var import_node_crypto3 = require("crypto");
|
|
10417
10492
|
var import_promises21 = __toESM(require("fs/promises"), 1);
|
|
10418
|
-
var
|
|
10419
|
-
var
|
|
10493
|
+
var import_node_os7 = __toESM(require("os"), 1);
|
|
10494
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
10420
10495
|
|
|
10421
10496
|
// src/hook-state.ts
|
|
10422
10497
|
var import_promises20 = __toESM(require("fs/promises"), 1);
|
|
10423
|
-
var
|
|
10424
|
-
var
|
|
10425
|
-
var
|
|
10426
|
-
function
|
|
10498
|
+
var import_node_os6 = __toESM(require("os"), 1);
|
|
10499
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
10500
|
+
var import_node_crypto2 = require("crypto");
|
|
10501
|
+
function stateRoot3() {
|
|
10427
10502
|
const configured = process.env.REMIX_CLAUDE_PLUGIN_HOOK_STATE_ROOT?.trim();
|
|
10428
|
-
return configured ||
|
|
10503
|
+
return configured || import_node_path9.default.join(import_node_os6.default.tmpdir(), "remix-claude-plugin-hooks");
|
|
10429
10504
|
}
|
|
10430
10505
|
function statePath(sessionId) {
|
|
10431
|
-
return
|
|
10506
|
+
return import_node_path9.default.join(stateRoot3(), `${sessionId}.json`);
|
|
10432
10507
|
}
|
|
10433
10508
|
function stateLockPath(sessionId) {
|
|
10434
|
-
return
|
|
10509
|
+
return import_node_path9.default.join(stateRoot3(), `${sessionId}.lock`);
|
|
10435
10510
|
}
|
|
10436
10511
|
function stateLockMetaPath(sessionId) {
|
|
10437
|
-
return
|
|
10512
|
+
return import_node_path9.default.join(stateLockPath(sessionId), "owner.json");
|
|
10438
10513
|
}
|
|
10439
10514
|
async function writeJsonAtomic2(filePath, value) {
|
|
10440
|
-
await import_promises20.default.mkdir(
|
|
10515
|
+
await import_promises20.default.mkdir(import_node_path9.default.dirname(filePath), { recursive: true });
|
|
10441
10516
|
const tmpPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
10442
10517
|
await import_promises20.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
10443
10518
|
await import_promises20.default.rename(tmpPath, filePath);
|
|
@@ -10490,11 +10565,11 @@ async function tryRemoveStaleStateLock(sessionId) {
|
|
|
10490
10565
|
async function acquireStateLock(sessionId) {
|
|
10491
10566
|
const lockPath = stateLockPath(sessionId);
|
|
10492
10567
|
const deadline = Date.now() + STATE_LOCK_WAIT_MS;
|
|
10493
|
-
await import_promises20.default.mkdir(
|
|
10568
|
+
await import_promises20.default.mkdir(stateRoot3(), { recursive: true });
|
|
10494
10569
|
while (true) {
|
|
10495
10570
|
try {
|
|
10496
10571
|
await import_promises20.default.mkdir(lockPath);
|
|
10497
|
-
const ownerId = (0,
|
|
10572
|
+
const ownerId = (0, import_node_crypto2.randomUUID)();
|
|
10498
10573
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10499
10574
|
const metadata = {
|
|
10500
10575
|
ownerId,
|
|
@@ -10551,7 +10626,7 @@ async function createPendingTurnState(params) {
|
|
|
10551
10626
|
return withStateLock(params.sessionId, async () => {
|
|
10552
10627
|
const state = {
|
|
10553
10628
|
sessionId: params.sessionId,
|
|
10554
|
-
turnId: (0,
|
|
10629
|
+
turnId: (0, import_node_crypto2.randomUUID)(),
|
|
10555
10630
|
prompt: params.prompt,
|
|
10556
10631
|
initialCwd: params.initialCwd?.trim() || null,
|
|
10557
10632
|
intent: params.intent,
|
|
@@ -10570,7 +10645,7 @@ async function createPendingTurnState(params) {
|
|
|
10570
10645
|
// package.json
|
|
10571
10646
|
var package_default = {
|
|
10572
10647
|
name: "@remixhq/claude-plugin",
|
|
10573
|
-
version: "0.1.
|
|
10648
|
+
version: "0.1.25",
|
|
10574
10649
|
description: "Claude Code plugin for Remix collaboration workflows",
|
|
10575
10650
|
homepage: "https://github.com/RemixDotOne/remix-claude-plugin",
|
|
10576
10651
|
license: "MIT",
|
|
@@ -10608,8 +10683,8 @@ var package_default = {
|
|
|
10608
10683
|
prepack: "npm run build"
|
|
10609
10684
|
},
|
|
10610
10685
|
dependencies: {
|
|
10611
|
-
"@remixhq/core": "^0.1.
|
|
10612
|
-
"@remixhq/mcp": "^0.1.
|
|
10686
|
+
"@remixhq/core": "^0.1.20",
|
|
10687
|
+
"@remixhq/mcp": "^0.1.20"
|
|
10613
10688
|
},
|
|
10614
10689
|
devDependencies: {
|
|
10615
10690
|
"@types/node": "^25.4.0",
|
|
@@ -10632,17 +10707,17 @@ var pluginMetadata = {
|
|
|
10632
10707
|
var MAX_LOG_BYTES = 512 * 1024;
|
|
10633
10708
|
function resolveClaudeRoot() {
|
|
10634
10709
|
const configured = process.env.CLAUDE_CONFIG_DIR?.trim();
|
|
10635
|
-
return configured ||
|
|
10710
|
+
return configured || import_node_path10.default.join(import_node_os7.default.homedir(), ".claude");
|
|
10636
10711
|
}
|
|
10637
10712
|
function resolvePluginDataDirName() {
|
|
10638
10713
|
return `${pluginMetadata.pluginId}-${pluginMetadata.pluginId}`;
|
|
10639
10714
|
}
|
|
10640
10715
|
function getHookDiagnosticsDirPath() {
|
|
10641
10716
|
const configured = process.env.REMIX_CLAUDE_PLUGIN_HOOK_DIAGNOSTICS_DIR?.trim();
|
|
10642
|
-
return configured ||
|
|
10717
|
+
return configured || import_node_path10.default.join(resolveClaudeRoot(), "plugins", "data", resolvePluginDataDirName());
|
|
10643
10718
|
}
|
|
10644
10719
|
function getHookDiagnosticsLogPath() {
|
|
10645
|
-
return
|
|
10720
|
+
return import_node_path10.default.join(getHookDiagnosticsDirPath(), "hooks.ndjson");
|
|
10646
10721
|
}
|
|
10647
10722
|
function toFieldValue(value) {
|
|
10648
10723
|
if (value === null) return null;
|
|
@@ -10680,13 +10755,13 @@ function summarizeText(value) {
|
|
|
10680
10755
|
return {
|
|
10681
10756
|
present: true,
|
|
10682
10757
|
length: trimmed.length,
|
|
10683
|
-
sha256Prefix: (0,
|
|
10758
|
+
sha256Prefix: (0, import_node_crypto3.createHash)("sha256").update(trimmed).digest("hex").slice(0, 12)
|
|
10684
10759
|
};
|
|
10685
10760
|
}
|
|
10686
10761
|
async function appendHookDiagnosticsEvent(params) {
|
|
10687
10762
|
try {
|
|
10688
10763
|
const logPath = getHookDiagnosticsLogPath();
|
|
10689
|
-
await import_promises21.default.mkdir(
|
|
10764
|
+
await import_promises21.default.mkdir(import_node_path10.default.dirname(logPath), { recursive: true });
|
|
10690
10765
|
await rotateLogIfNeeded(logPath);
|
|
10691
10766
|
const event = {
|
|
10692
10767
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -10733,18 +10808,27 @@ var RECOMMENDED_USER_COMMAND = {
|
|
|
10733
10808
|
pull_required: "remix collab sync",
|
|
10734
10809
|
baseline_missing: "remix collab init"
|
|
10735
10810
|
};
|
|
10736
|
-
var SPAWN_LOCK_REL = (cmdSlug) => import_node_path10.default.join(".remix", `.${cmdSlug}-spawning`);
|
|
10737
|
-
var SPAWN_LOG_REL = (cmdSlug) => import_node_path10.default.join(".remix", `${cmdSlug}.log`);
|
|
10738
10811
|
var SPAWN_THROTTLE_MS = 5 * 60 * 1e3;
|
|
10739
10812
|
function commandSlug(args) {
|
|
10740
10813
|
return args.join("-").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
10741
10814
|
}
|
|
10815
|
+
function stateRoot4() {
|
|
10816
|
+
const configured = process.env.REMIX_CLAUDE_PLUGIN_HOOK_STATE_ROOT?.trim();
|
|
10817
|
+
return configured || import_node_path11.default.join(import_node_os8.default.tmpdir(), "remix-claude-plugin-hooks");
|
|
10818
|
+
}
|
|
10819
|
+
function sha256Hex3(value) {
|
|
10820
|
+
return (0, import_node_crypto4.createHash)("sha256").update(value).digest("hex");
|
|
10821
|
+
}
|
|
10822
|
+
function spawnLockPath(repoRoot, cmdSlug) {
|
|
10823
|
+
if (cmdSlug === "collab-init") {
|
|
10824
|
+
return collabInitSpawnLockPath(repoRoot);
|
|
10825
|
+
}
|
|
10826
|
+
return import_node_path11.default.join(stateRoot4(), "auto-fix-spawn-locks", sha256Hex3(repoRoot), `${cmdSlug}.lock`);
|
|
10827
|
+
}
|
|
10742
10828
|
function spawnFixDetached(repoRoot, args) {
|
|
10743
10829
|
const slug = commandSlug(args);
|
|
10744
10830
|
const command = `remix ${args.join(" ")}`;
|
|
10745
|
-
const
|
|
10746
|
-
const lockPath = import_node_path10.default.join(repoRoot, SPAWN_LOCK_REL(slug));
|
|
10747
|
-
const logPath = import_node_path10.default.join(repoRoot, SPAWN_LOG_REL(slug));
|
|
10831
|
+
const lockPath = spawnLockPath(repoRoot, slug);
|
|
10748
10832
|
try {
|
|
10749
10833
|
if ((0, import_node_fs6.existsSync)(lockPath)) {
|
|
10750
10834
|
const ageMs = Date.now() - (0, import_node_fs6.statSync)(lockPath).mtimeMs;
|
|
@@ -10755,27 +10839,14 @@ function spawnFixDetached(repoRoot, args) {
|
|
|
10755
10839
|
} catch {
|
|
10756
10840
|
}
|
|
10757
10841
|
try {
|
|
10758
|
-
(0, import_node_fs6.mkdirSync)(
|
|
10842
|
+
(0, import_node_fs6.mkdirSync)(import_node_path11.default.dirname(lockPath), { recursive: true });
|
|
10759
10843
|
} catch {
|
|
10760
10844
|
}
|
|
10761
|
-
let out;
|
|
10762
|
-
let err;
|
|
10763
|
-
try {
|
|
10764
|
-
out = (0, import_node_fs6.openSync)(logPath, "a");
|
|
10765
|
-
err = (0, import_node_fs6.openSync)(logPath, "a");
|
|
10766
|
-
} catch (logErr) {
|
|
10767
|
-
return {
|
|
10768
|
-
kind: "spawn_failed",
|
|
10769
|
-
command,
|
|
10770
|
-
reason: "log_open_failed",
|
|
10771
|
-
message: logErr instanceof Error ? logErr.message : String(logErr)
|
|
10772
|
-
};
|
|
10773
|
-
}
|
|
10774
10845
|
try {
|
|
10775
10846
|
const child = (0, import_node_child_process6.spawn)("remix", [...args], {
|
|
10776
10847
|
cwd: repoRoot,
|
|
10777
10848
|
detached: true,
|
|
10778
|
-
stdio:
|
|
10849
|
+
stdio: "ignore",
|
|
10779
10850
|
env: { ...process.env, REMIX_AUTO_FIX_SPAWN: "1" }
|
|
10780
10851
|
});
|
|
10781
10852
|
child.unref();
|
|
@@ -10784,7 +10855,7 @@ function spawnFixDetached(repoRoot, args) {
|
|
|
10784
10855
|
(0, import_node_fs6.utimesSync)(lockPath, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date());
|
|
10785
10856
|
} catch {
|
|
10786
10857
|
}
|
|
10787
|
-
return { kind: "spawned", command, pid: child.pid
|
|
10858
|
+
return { kind: "spawned", command, pid: child.pid };
|
|
10788
10859
|
} catch (spawnErr) {
|
|
10789
10860
|
return {
|
|
10790
10861
|
kind: "spawn_failed",
|
|
@@ -10840,7 +10911,6 @@ async function dispatchFinalizeFailure(input) {
|
|
|
10840
10911
|
preflightCode: input.preflightCode,
|
|
10841
10912
|
command: "command" in outcome ? outcome.command : null,
|
|
10842
10913
|
pid: outcome.kind === "spawned" ? outcome.pid ?? null : null,
|
|
10843
|
-
logPath: outcome.kind === "spawned" ? outcome.logPath : null,
|
|
10844
10914
|
recommendedCommand
|
|
10845
10915
|
},
|
|
10846
10916
|
message: outcome.kind === "spawn_failed" ? outcome.message : null
|
|
@@ -10853,7 +10923,7 @@ function mergeOutcomeIntoMarker(existing, outcome) {
|
|
|
10853
10923
|
status: "in_progress",
|
|
10854
10924
|
command: outcome.command,
|
|
10855
10925
|
pid: outcome.pid ?? null,
|
|
10856
|
-
logPath:
|
|
10926
|
+
logPath: null,
|
|
10857
10927
|
attemptedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10858
10928
|
failureMessage: null
|
|
10859
10929
|
};
|
|
@@ -10881,7 +10951,7 @@ function mergeOutcomeIntoMarker(existing, outcome) {
|
|
|
10881
10951
|
return existing;
|
|
10882
10952
|
}
|
|
10883
10953
|
|
|
10884
|
-
// node_modules/@remixhq/core/dist/chunk-
|
|
10954
|
+
// node_modules/@remixhq/core/dist/chunk-C2FOZ3O7.js
|
|
10885
10955
|
async function readJsonSafe(res) {
|
|
10886
10956
|
const ct = res.headers.get("content-type") ?? "";
|
|
10887
10957
|
if (!ct.toLowerCase().includes("application/json")) return null;
|
|
@@ -10900,7 +10970,7 @@ function createApiClient(config, opts) {
|
|
|
10900
10970
|
const ms = typeof timeoutMs === "number" && timeoutMs > 0 ? timeoutMs : defaultTimeoutMs;
|
|
10901
10971
|
return ms != null ? AbortSignal.timeout(ms) : void 0;
|
|
10902
10972
|
}
|
|
10903
|
-
async function request(
|
|
10973
|
+
async function request(path18, init, opts2) {
|
|
10904
10974
|
if (!tokenProvider) {
|
|
10905
10975
|
throw new RemixError("API client is missing a token provider.", {
|
|
10906
10976
|
exitCode: 1,
|
|
@@ -10908,7 +10978,7 @@ function createApiClient(config, opts) {
|
|
|
10908
10978
|
});
|
|
10909
10979
|
}
|
|
10910
10980
|
const auth = await tokenProvider();
|
|
10911
|
-
const url = new URL(
|
|
10981
|
+
const url = new URL(path18, config.apiUrl).toString();
|
|
10912
10982
|
const doFetch = async (bearer) => fetch(url, {
|
|
10913
10983
|
...init,
|
|
10914
10984
|
signal: makeTimeoutSignal(opts2?.timeoutMs),
|
|
@@ -10937,7 +11007,7 @@ function createApiClient(config, opts) {
|
|
|
10937
11007
|
const json = await readJsonSafe(res);
|
|
10938
11008
|
return json ?? null;
|
|
10939
11009
|
}
|
|
10940
|
-
async function requestBinary(
|
|
11010
|
+
async function requestBinary(path18, init, opts2) {
|
|
10941
11011
|
if (!tokenProvider) {
|
|
10942
11012
|
throw new RemixError("API client is missing a token provider.", {
|
|
10943
11013
|
exitCode: 1,
|
|
@@ -10945,7 +11015,7 @@ function createApiClient(config, opts) {
|
|
|
10945
11015
|
});
|
|
10946
11016
|
}
|
|
10947
11017
|
const auth = await tokenProvider();
|
|
10948
|
-
const url = new URL(
|
|
11018
|
+
const url = new URL(path18, config.apiUrl).toString();
|
|
10949
11019
|
const doFetch = async (bearer) => fetch(url, {
|
|
10950
11020
|
...init,
|
|
10951
11021
|
signal: makeTimeoutSignal(opts2?.timeoutMs),
|
|
@@ -11075,6 +11145,14 @@ function createApiClient(config, opts) {
|
|
|
11075
11145
|
method: "POST",
|
|
11076
11146
|
body: JSON.stringify(payload)
|
|
11077
11147
|
}),
|
|
11148
|
+
listChangeSteps: (appId, params) => {
|
|
11149
|
+
const qs = new URLSearchParams();
|
|
11150
|
+
if (params?.limit !== void 0) qs.set("limit", String(params.limit));
|
|
11151
|
+
if (params?.offset !== void 0) qs.set("offset", String(params.offset));
|
|
11152
|
+
if (params?.idempotencyKey) qs.set("idempotencyKey", params.idempotencyKey);
|
|
11153
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
11154
|
+
return request(`/v1/apps/${encodeURIComponent(appId)}/change-steps${suffix}`, { method: "GET" });
|
|
11155
|
+
},
|
|
11078
11156
|
createCollabTurn: (appId, payload) => request(`/v1/apps/${encodeURIComponent(appId)}/collab-turns`, {
|
|
11079
11157
|
method: "POST",
|
|
11080
11158
|
body: JSON.stringify(payload)
|
|
@@ -11800,8 +11878,8 @@ function getErrorMap() {
|
|
|
11800
11878
|
|
|
11801
11879
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
11802
11880
|
var makeIssue = (params) => {
|
|
11803
|
-
const { data, path:
|
|
11804
|
-
const fullPath = [...
|
|
11881
|
+
const { data, path: path18, errorMaps, issueData } = params;
|
|
11882
|
+
const fullPath = [...path18, ...issueData.path || []];
|
|
11805
11883
|
const fullIssue = {
|
|
11806
11884
|
...issueData,
|
|
11807
11885
|
path: fullPath
|
|
@@ -11917,11 +11995,11 @@ var errorUtil;
|
|
|
11917
11995
|
|
|
11918
11996
|
// node_modules/zod/v3/types.js
|
|
11919
11997
|
var ParseInputLazyPath = class {
|
|
11920
|
-
constructor(parent, value,
|
|
11998
|
+
constructor(parent, value, path18, key) {
|
|
11921
11999
|
this._cachedPath = [];
|
|
11922
12000
|
this.parent = parent;
|
|
11923
12001
|
this.data = value;
|
|
11924
|
-
this._path =
|
|
12002
|
+
this._path = path18;
|
|
11925
12003
|
this._key = key;
|
|
11926
12004
|
}
|
|
11927
12005
|
get path() {
|
|
@@ -24469,8 +24547,8 @@ var IcebergError = class extends Error {
|
|
|
24469
24547
|
return this.status === 419;
|
|
24470
24548
|
}
|
|
24471
24549
|
};
|
|
24472
|
-
function buildUrl(baseUrl,
|
|
24473
|
-
const url = new URL(
|
|
24550
|
+
function buildUrl(baseUrl, path18, query) {
|
|
24551
|
+
const url = new URL(path18, baseUrl);
|
|
24474
24552
|
if (query) {
|
|
24475
24553
|
for (const [key, value] of Object.entries(query)) {
|
|
24476
24554
|
if (value !== void 0) {
|
|
@@ -24500,12 +24578,12 @@ function createFetchClient(options) {
|
|
|
24500
24578
|
return {
|
|
24501
24579
|
async request({
|
|
24502
24580
|
method,
|
|
24503
|
-
path:
|
|
24581
|
+
path: path18,
|
|
24504
24582
|
query,
|
|
24505
24583
|
body,
|
|
24506
24584
|
headers
|
|
24507
24585
|
}) {
|
|
24508
|
-
const url = buildUrl(options.baseUrl,
|
|
24586
|
+
const url = buildUrl(options.baseUrl, path18, query);
|
|
24509
24587
|
const authHeaders = await buildAuthHeaders(options.auth);
|
|
24510
24588
|
const res = await fetchFn(url, {
|
|
24511
24589
|
method,
|
|
@@ -25343,7 +25421,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25343
25421
|
* @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
|
|
25344
25422
|
* @param fileBody The body of the file to be stored in the bucket.
|
|
25345
25423
|
*/
|
|
25346
|
-
async uploadOrUpdate(method,
|
|
25424
|
+
async uploadOrUpdate(method, path18, fileBody, fileOptions) {
|
|
25347
25425
|
var _this = this;
|
|
25348
25426
|
return _this.handleOperation(async () => {
|
|
25349
25427
|
let body;
|
|
@@ -25367,7 +25445,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25367
25445
|
if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
|
|
25368
25446
|
}
|
|
25369
25447
|
if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) for (const [key, value] of Object.entries(fileOptions.headers)) headers = setHeader(headers, key, value);
|
|
25370
|
-
const cleanPath = _this._removeEmptyFolders(
|
|
25448
|
+
const cleanPath = _this._removeEmptyFolders(path18);
|
|
25371
25449
|
const _path = _this._getFinalPath(cleanPath);
|
|
25372
25450
|
const data = await (method == "PUT" ? put : post)(_this.fetch, `${_this.url}/object/${_path}`, body, _objectSpread22({ headers }, (options === null || options === void 0 ? void 0 : options.duplex) ? { duplex: options.duplex } : {}));
|
|
25373
25451
|
return {
|
|
@@ -25428,8 +25506,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25428
25506
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
25429
25507
|
* - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Upload file using `ArrayBuffer` from base64 file data instead, see example below.
|
|
25430
25508
|
*/
|
|
25431
|
-
async upload(
|
|
25432
|
-
return this.uploadOrUpdate("POST",
|
|
25509
|
+
async upload(path18, fileBody, fileOptions) {
|
|
25510
|
+
return this.uploadOrUpdate("POST", path18, fileBody, fileOptions);
|
|
25433
25511
|
}
|
|
25434
25512
|
/**
|
|
25435
25513
|
* Upload a file with a token generated from `createSignedUploadUrl`.
|
|
@@ -25468,9 +25546,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25468
25546
|
* - `objects` table permissions: none
|
|
25469
25547
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
25470
25548
|
*/
|
|
25471
|
-
async uploadToSignedUrl(
|
|
25549
|
+
async uploadToSignedUrl(path18, token, fileBody, fileOptions) {
|
|
25472
25550
|
var _this3 = this;
|
|
25473
|
-
const cleanPath = _this3._removeEmptyFolders(
|
|
25551
|
+
const cleanPath = _this3._removeEmptyFolders(path18);
|
|
25474
25552
|
const _path = _this3._getFinalPath(cleanPath);
|
|
25475
25553
|
const url = new URL(_this3.url + `/object/upload/sign/${_path}`);
|
|
25476
25554
|
url.searchParams.set("token", token);
|
|
@@ -25532,10 +25610,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25532
25610
|
* - `objects` table permissions: `insert`
|
|
25533
25611
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
25534
25612
|
*/
|
|
25535
|
-
async createSignedUploadUrl(
|
|
25613
|
+
async createSignedUploadUrl(path18, options) {
|
|
25536
25614
|
var _this4 = this;
|
|
25537
25615
|
return _this4.handleOperation(async () => {
|
|
25538
|
-
let _path = _this4._getFinalPath(
|
|
25616
|
+
let _path = _this4._getFinalPath(path18);
|
|
25539
25617
|
const headers = _objectSpread22({}, _this4.headers);
|
|
25540
25618
|
if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
|
|
25541
25619
|
const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
|
|
@@ -25544,7 +25622,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25544
25622
|
if (!token) throw new StorageError("No token returned by API");
|
|
25545
25623
|
return {
|
|
25546
25624
|
signedUrl: url.toString(),
|
|
25547
|
-
path:
|
|
25625
|
+
path: path18,
|
|
25548
25626
|
token
|
|
25549
25627
|
};
|
|
25550
25628
|
});
|
|
@@ -25600,8 +25678,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25600
25678
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
25601
25679
|
* - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Update file using `ArrayBuffer` from base64 file data instead, see example below.
|
|
25602
25680
|
*/
|
|
25603
|
-
async update(
|
|
25604
|
-
return this.uploadOrUpdate("PUT",
|
|
25681
|
+
async update(path18, fileBody, fileOptions) {
|
|
25682
|
+
return this.uploadOrUpdate("PUT", path18, fileBody, fileOptions);
|
|
25605
25683
|
}
|
|
25606
25684
|
/**
|
|
25607
25685
|
* Moves an existing file to a new path in the same bucket.
|
|
@@ -25749,10 +25827,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25749
25827
|
* - `objects` table permissions: `select`
|
|
25750
25828
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
25751
25829
|
*/
|
|
25752
|
-
async createSignedUrl(
|
|
25830
|
+
async createSignedUrl(path18, expiresIn, options) {
|
|
25753
25831
|
var _this8 = this;
|
|
25754
25832
|
return _this8.handleOperation(async () => {
|
|
25755
|
-
let _path = _this8._getFinalPath(
|
|
25833
|
+
let _path = _this8._getFinalPath(path18);
|
|
25756
25834
|
const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
|
|
25757
25835
|
let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
|
|
25758
25836
|
const query = new URLSearchParams();
|
|
@@ -25886,13 +25964,13 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25886
25964
|
* - `objects` table permissions: `select`
|
|
25887
25965
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
25888
25966
|
*/
|
|
25889
|
-
download(
|
|
25967
|
+
download(path18, options, parameters) {
|
|
25890
25968
|
const renderPath = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0 ? "render/image/authenticated" : "object";
|
|
25891
25969
|
const query = new URLSearchParams();
|
|
25892
25970
|
if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
|
|
25893
25971
|
if ((options === null || options === void 0 ? void 0 : options.cacheNonce) != null) query.set("cacheNonce", String(options.cacheNonce));
|
|
25894
25972
|
const queryString = query.toString();
|
|
25895
|
-
const _path = this._getFinalPath(
|
|
25973
|
+
const _path = this._getFinalPath(path18);
|
|
25896
25974
|
const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ""}`, {
|
|
25897
25975
|
headers: this.headers,
|
|
25898
25976
|
noResolveJson: true
|
|
@@ -25922,9 +26000,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25922
26000
|
* }
|
|
25923
26001
|
* ```
|
|
25924
26002
|
*/
|
|
25925
|
-
async info(
|
|
26003
|
+
async info(path18) {
|
|
25926
26004
|
var _this10 = this;
|
|
25927
|
-
const _path = _this10._getFinalPath(
|
|
26005
|
+
const _path = _this10._getFinalPath(path18);
|
|
25928
26006
|
return _this10.handleOperation(async () => {
|
|
25929
26007
|
return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
|
|
25930
26008
|
});
|
|
@@ -25944,9 +26022,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
25944
26022
|
* .exists('folder/avatar1.png')
|
|
25945
26023
|
* ```
|
|
25946
26024
|
*/
|
|
25947
|
-
async exists(
|
|
26025
|
+
async exists(path18) {
|
|
25948
26026
|
var _this11 = this;
|
|
25949
|
-
const _path = _this11._getFinalPath(
|
|
26027
|
+
const _path = _this11._getFinalPath(path18);
|
|
25950
26028
|
try {
|
|
25951
26029
|
await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
|
|
25952
26030
|
return {
|
|
@@ -26024,8 +26102,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
26024
26102
|
* - `objects` table permissions: none
|
|
26025
26103
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
26026
26104
|
*/
|
|
26027
|
-
getPublicUrl(
|
|
26028
|
-
const _path = this._getFinalPath(
|
|
26105
|
+
getPublicUrl(path18, options) {
|
|
26106
|
+
const _path = this._getFinalPath(path18);
|
|
26029
26107
|
const query = new URLSearchParams();
|
|
26030
26108
|
if (options === null || options === void 0 ? void 0 : options.download) query.set("download", options.download === true ? "" : options.download);
|
|
26031
26109
|
if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
|
|
@@ -26162,10 +26240,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
26162
26240
|
* - `objects` table permissions: `select`
|
|
26163
26241
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
26164
26242
|
*/
|
|
26165
|
-
async list(
|
|
26243
|
+
async list(path18, options, parameters) {
|
|
26166
26244
|
var _this13 = this;
|
|
26167
26245
|
return _this13.handleOperation(async () => {
|
|
26168
|
-
const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix:
|
|
26246
|
+
const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path18 || "" });
|
|
26169
26247
|
return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
|
|
26170
26248
|
});
|
|
26171
26249
|
}
|
|
@@ -26229,11 +26307,11 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
26229
26307
|
if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
|
|
26230
26308
|
return btoa(data);
|
|
26231
26309
|
}
|
|
26232
|
-
_getFinalPath(
|
|
26233
|
-
return `${this.bucketId}/${
|
|
26310
|
+
_getFinalPath(path18) {
|
|
26311
|
+
return `${this.bucketId}/${path18.replace(/^\/+/, "")}`;
|
|
26234
26312
|
}
|
|
26235
|
-
_removeEmptyFolders(
|
|
26236
|
-
return
|
|
26313
|
+
_removeEmptyFolders(path18) {
|
|
26314
|
+
return path18.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
|
|
26237
26315
|
}
|
|
26238
26316
|
/** Modifies the `query`, appending values the from `transform` */
|
|
26239
26317
|
applyTransformOptsToQuery(query, transform) {
|
|
@@ -35611,11 +35689,11 @@ function isPidAlive(pid) {
|
|
|
35611
35689
|
}
|
|
35612
35690
|
}
|
|
35613
35691
|
function repoLockFileName(repoRoot) {
|
|
35614
|
-
const hash = (0,
|
|
35692
|
+
const hash = (0, import_node_crypto5.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 16);
|
|
35615
35693
|
return `.drainer-${hash}.lock`;
|
|
35616
35694
|
}
|
|
35617
35695
|
function repoLockPath(repoRoot) {
|
|
35618
|
-
return
|
|
35696
|
+
return import_node_path12.default.join(getDeferredTurnDirPath(), repoLockFileName(repoRoot));
|
|
35619
35697
|
}
|
|
35620
35698
|
async function readDrainLockMetadata(lockPath) {
|
|
35621
35699
|
const raw = await import_promises23.default.readFile(lockPath, "utf8").catch(() => null);
|
|
@@ -35637,7 +35715,7 @@ async function writeDrainLockMetadata(lockPath, metadata) {
|
|
|
35637
35715
|
}
|
|
35638
35716
|
async function tryAcquireDrainLock(repoRoot) {
|
|
35639
35717
|
const lockPath = repoLockPath(repoRoot);
|
|
35640
|
-
await import_promises23.default.mkdir(
|
|
35718
|
+
await import_promises23.default.mkdir(import_node_path12.default.dirname(lockPath), { recursive: true });
|
|
35641
35719
|
const existingMeta = await readDrainLockMetadata(lockPath);
|
|
35642
35720
|
if (existingMeta) {
|
|
35643
35721
|
const lockStat = await import_promises23.default.stat(lockPath).catch(() => null);
|
|
@@ -36034,7 +36112,7 @@ function spawnDeferredTurnDrainer(repoRoot) {
|
|
|
36034
36112
|
|
|
36035
36113
|
// src/hook-utils.ts
|
|
36036
36114
|
var import_promises24 = __toESM(require("fs/promises"), 1);
|
|
36037
|
-
var
|
|
36115
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
36038
36116
|
async function readJsonStdin() {
|
|
36039
36117
|
const chunks = [];
|
|
36040
36118
|
for await (const chunk of process.stdin) {
|
|
@@ -36060,16 +36138,16 @@ function extractString(input, keys) {
|
|
|
36060
36138
|
}
|
|
36061
36139
|
async function findBoundRepo(startPath) {
|
|
36062
36140
|
if (!startPath) return null;
|
|
36063
|
-
let current =
|
|
36141
|
+
let current = import_node_path13.default.resolve(startPath);
|
|
36064
36142
|
let stats = await import_promises24.default.stat(current).catch(() => null);
|
|
36065
36143
|
if (stats?.isFile()) {
|
|
36066
|
-
current =
|
|
36144
|
+
current = import_node_path13.default.dirname(current);
|
|
36067
36145
|
}
|
|
36068
36146
|
while (true) {
|
|
36069
|
-
const bindingPath =
|
|
36147
|
+
const bindingPath = import_node_path13.default.join(current, ".remix", "config.json");
|
|
36070
36148
|
const bindingStats = await import_promises24.default.stat(bindingPath).catch(() => null);
|
|
36071
36149
|
if (bindingStats?.isFile()) return current;
|
|
36072
|
-
const parent =
|
|
36150
|
+
const parent = import_node_path13.default.dirname(current);
|
|
36073
36151
|
if (parent === current) return null;
|
|
36074
36152
|
current = parent;
|
|
36075
36153
|
}
|
|
@@ -36093,8 +36171,6 @@ function buildRuntimeStatusOverride() {
|
|
|
36093
36171
|
"Use `remix_collab_drain_finalize_queue` only for explicit recovery flows, such as status reporting `await_finalize` before a merge-related operation."
|
|
36094
36172
|
].join("\n");
|
|
36095
36173
|
}
|
|
36096
|
-
var COLLAB_INIT_LOG_REL = import_node_path13.default.join(".remix", "collab-init.log");
|
|
36097
|
-
var COLLAB_INIT_SPAWN_LOCK_REL = import_node_path13.default.join(".remix", ".collab-init-spawning");
|
|
36098
36174
|
var COLLAB_INIT_SPAWN_LOCK_STALE_MS = 90 * 1e3;
|
|
36099
36175
|
function isPidAlive2(pid) {
|
|
36100
36176
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -36105,31 +36181,42 @@ function isPidAlive2(pid) {
|
|
|
36105
36181
|
return false;
|
|
36106
36182
|
}
|
|
36107
36183
|
}
|
|
36108
|
-
function
|
|
36184
|
+
function readSpawnLock(spawnLockPath2) {
|
|
36109
36185
|
try {
|
|
36110
|
-
const raw = (0, import_node_fs7.readFileSync)(
|
|
36186
|
+
const raw = (0, import_node_fs7.readFileSync)(spawnLockPath2, "utf8").trim();
|
|
36111
36187
|
if (!raw) return null;
|
|
36188
|
+
if (raw.startsWith("{")) {
|
|
36189
|
+
const parsed = JSON.parse(raw);
|
|
36190
|
+
const pid2 = Number(parsed?.pid ?? 0);
|
|
36191
|
+
return {
|
|
36192
|
+
pid: Number.isFinite(pid2) && pid2 > 0 ? pid2 : null,
|
|
36193
|
+
branchName: typeof parsed?.branchName === "string" && parsed.branchName.trim() ? parsed.branchName : null
|
|
36194
|
+
};
|
|
36195
|
+
}
|
|
36112
36196
|
const pid = Number.parseInt(raw, 10);
|
|
36113
|
-
return
|
|
36197
|
+
return {
|
|
36198
|
+
pid: Number.isFinite(pid) && pid > 0 ? pid : null,
|
|
36199
|
+
branchName: null
|
|
36200
|
+
};
|
|
36114
36201
|
} catch {
|
|
36115
36202
|
return null;
|
|
36116
36203
|
}
|
|
36117
36204
|
}
|
|
36118
|
-
function maybeAutoSpawnBranchInit(repoRoot) {
|
|
36119
|
-
const
|
|
36120
|
-
const spawnLockPath = import_node_path13.default.join(repoRoot, COLLAB_INIT_SPAWN_LOCK_REL);
|
|
36121
|
-
const logPath = import_node_path13.default.join(repoRoot, COLLAB_INIT_LOG_REL);
|
|
36205
|
+
function maybeAutoSpawnBranchInit(repoRoot, branchName) {
|
|
36206
|
+
const spawnLockPath2 = collabInitSpawnLockPath(repoRoot);
|
|
36122
36207
|
try {
|
|
36123
|
-
if ((0, import_node_fs7.existsSync)(
|
|
36124
|
-
const
|
|
36208
|
+
if ((0, import_node_fs7.existsSync)(spawnLockPath2)) {
|
|
36209
|
+
const lock = readSpawnLock(spawnLockPath2);
|
|
36210
|
+
const lockPid = lock?.pid ?? null;
|
|
36125
36211
|
const lockAlive = lockPid !== null && isPidAlive2(lockPid);
|
|
36126
|
-
const
|
|
36127
|
-
|
|
36212
|
+
const sameBranch = !lock?.branchName || !branchName || lock.branchName === branchName;
|
|
36213
|
+
const ageMs = Date.now() - (0, import_node_fs7.statSync)(spawnLockPath2).mtimeMs;
|
|
36214
|
+
if (lockAlive && sameBranch && ageMs < COLLAB_INIT_SPAWN_LOCK_STALE_MS) {
|
|
36128
36215
|
return { spawned: false, reason: "spawn_lock_held" };
|
|
36129
36216
|
}
|
|
36130
36217
|
if (!lockAlive) {
|
|
36131
36218
|
try {
|
|
36132
|
-
(0, import_node_fs7.unlinkSync)(
|
|
36219
|
+
(0, import_node_fs7.unlinkSync)(spawnLockPath2);
|
|
36133
36220
|
} catch {
|
|
36134
36221
|
}
|
|
36135
36222
|
}
|
|
@@ -36137,35 +36224,32 @@ function maybeAutoSpawnBranchInit(repoRoot) {
|
|
|
36137
36224
|
} catch {
|
|
36138
36225
|
}
|
|
36139
36226
|
try {
|
|
36140
|
-
(0, import_node_fs7.mkdirSync)(
|
|
36227
|
+
(0, import_node_fs7.mkdirSync)(import_node_path14.default.dirname(spawnLockPath2), { recursive: true });
|
|
36141
36228
|
} catch {
|
|
36142
36229
|
}
|
|
36143
|
-
let out;
|
|
36144
|
-
let err;
|
|
36145
|
-
try {
|
|
36146
|
-
out = (0, import_node_fs7.openSync)(logPath, "a");
|
|
36147
|
-
err = (0, import_node_fs7.openSync)(logPath, "a");
|
|
36148
|
-
} catch (logErr) {
|
|
36149
|
-
return {
|
|
36150
|
-
spawned: false,
|
|
36151
|
-
reason: "log_open_failed",
|
|
36152
|
-
message: logErr instanceof Error ? logErr.message : String(logErr)
|
|
36153
|
-
};
|
|
36154
|
-
}
|
|
36155
36230
|
try {
|
|
36156
36231
|
const child = (0, import_node_child_process8.spawn)("remix", ["collab", "init"], {
|
|
36157
36232
|
cwd: repoRoot,
|
|
36158
36233
|
detached: true,
|
|
36159
|
-
stdio:
|
|
36234
|
+
stdio: "ignore",
|
|
36160
36235
|
env: { ...process.env, REMIX_COLLAB_INIT_AUTO_SPAWN: "1" }
|
|
36161
36236
|
});
|
|
36162
36237
|
child.unref();
|
|
36163
36238
|
try {
|
|
36164
|
-
|
|
36165
|
-
|
|
36239
|
+
const lock = {
|
|
36240
|
+
schemaVersion: 1,
|
|
36241
|
+
pid: child.pid ?? null,
|
|
36242
|
+
branchName,
|
|
36243
|
+
repoRoot,
|
|
36244
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36245
|
+
command: "remix collab init"
|
|
36246
|
+
};
|
|
36247
|
+
(0, import_node_fs7.writeFileSync)(spawnLockPath2, `${JSON.stringify(lock, null, 2)}
|
|
36248
|
+
`, "utf8");
|
|
36249
|
+
(0, import_node_fs7.utimesSync)(spawnLockPath2, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date());
|
|
36166
36250
|
} catch {
|
|
36167
36251
|
}
|
|
36168
|
-
return { spawned: true, pid: child.pid
|
|
36252
|
+
return { spawned: true, pid: child.pid };
|
|
36169
36253
|
} catch (spawnErr) {
|
|
36170
36254
|
return {
|
|
36171
36255
|
spawned: false,
|
|
@@ -36174,14 +36258,13 @@ function maybeAutoSpawnBranchInit(repoRoot) {
|
|
|
36174
36258
|
};
|
|
36175
36259
|
}
|
|
36176
36260
|
}
|
|
36177
|
-
function buildBranchInitContextMessage(branch, repoRoot
|
|
36261
|
+
function buildBranchInitContextMessage(branch, repoRoot) {
|
|
36178
36262
|
const branchLabel = branch ? `\`${branch}\`` : "the current branch";
|
|
36179
36263
|
return [
|
|
36180
36264
|
"[Remix recovery in progress]:",
|
|
36181
36265
|
`Remix is initializing recording for ${branchLabel} in ${repoRoot} in the background.`,
|
|
36182
36266
|
"This turn may be recorded retroactively once init finishes (it may appear in the timeline with a small delay).",
|
|
36183
|
-
"Do NOT call any Remix MCP tool to initialize, repair, or sync this branch \u2014 the plugin is handling it automatically."
|
|
36184
|
-
`Init log: ${logPath}`
|
|
36267
|
+
"Do NOT call any Remix MCP tool to initialize, repair, or sync this branch \u2014 the plugin is handling it automatically."
|
|
36185
36268
|
].join("\n");
|
|
36186
36269
|
}
|
|
36187
36270
|
var STALE_MARKER_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -36380,9 +36463,9 @@ async function runHookUserPrompt(payload) {
|
|
|
36380
36463
|
}
|
|
36381
36464
|
if (isCurrentBranchUnbound) {
|
|
36382
36465
|
const currentBranch = bindingState?.currentBranch ?? null;
|
|
36383
|
-
const outcome = maybeAutoSpawnBranchInit(boundRepo);
|
|
36466
|
+
const outcome = maybeAutoSpawnBranchInit(boundRepo, currentBranch);
|
|
36384
36467
|
if (outcome.spawned) {
|
|
36385
|
-
advisorySections.push(buildBranchInitContextMessage(currentBranch, boundRepo
|
|
36468
|
+
advisorySections.push(buildBranchInitContextMessage(currentBranch, boundRepo));
|
|
36386
36469
|
await appendHookDiagnosticsEvent({
|
|
36387
36470
|
hook: "UserPromptSubmit",
|
|
36388
36471
|
sessionId,
|
|
@@ -36393,8 +36476,7 @@ async function runHookUserPrompt(payload) {
|
|
|
36393
36476
|
fields: {
|
|
36394
36477
|
currentBranch,
|
|
36395
36478
|
knownBoundBranchCount: knownBoundBranches.length,
|
|
36396
|
-
pid: outcome.pid ?? null
|
|
36397
|
-
logPath: outcome.logPath
|
|
36479
|
+
pid: outcome.pid ?? null
|
|
36398
36480
|
}
|
|
36399
36481
|
});
|
|
36400
36482
|
} else {
|