offsite 0.0.63 → 0.0.65

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.
@@ -301,6 +301,7 @@ function findCodex() {
301
301
  var AppServer = class {
302
302
  home;
303
303
  threads = /* @__PURE__ */ new Map();
304
+ agents = /* @__PURE__ */ new Map();
304
305
  child;
305
306
  waiting = /* @__PURE__ */ new Map();
306
307
  ready;
@@ -417,9 +418,16 @@ var AppServer = class {
417
418
  }
418
419
  const params = message.params ?? {};
419
420
  const listener = typeof params.threadId === "string" ? this.threads.get(params.threadId) : void 0;
421
+ const root = this.rootOf(params.threadId);
422
+ const owner = root === void 0 ? void 0 : this.threads.get(root);
423
+ if (root !== void 0) this.linkAgents(root, params.item);
420
424
  if (message.id !== void 0) {
421
- if (listener === void 0) this.replyError(message.id, `offsite does not handle ${message.method}`);
422
- else listener.request(message.id, message.method, params);
425
+ if (owner === void 0) this.replyError(message.id, `offsite does not handle ${message.method}`);
426
+ else owner.request(message.id, message.method, params);
427
+ return;
428
+ }
429
+ if (message.method === "serverRequest/resolved") {
430
+ owner?.notify(message.method, params);
423
431
  return;
424
432
  }
425
433
  if (message.method === "account/rateLimits/updated") {
@@ -428,6 +436,14 @@ var AppServer = class {
428
436
  }
429
437
  listener?.notify(message.method, params);
430
438
  }
439
+ rootOf(threadId) {
440
+ if (typeof threadId !== "string") return;
441
+ return this.threads.has(threadId) ? threadId : this.agents.get(threadId);
442
+ }
443
+ linkAgents(root, item) {
444
+ if (item?.type === "subAgentActivity") this.agents.set(item.agentThreadId, root);
445
+ if (item?.type === "collabAgentToolCall") for (const agent of item.receiverThreadIds) this.agents.set(agent, root);
446
+ }
431
447
  armIdleTimer() {
432
448
  clearTimeout(this.idleTimer);
433
449
  this.idleTimer = setTimeout(() => {
@@ -1032,7 +1048,8 @@ function configInfo() {
1032
1048
  defaults: defaults$1,
1033
1049
  platform: platform(),
1034
1050
  defaultShell: defaultShell(),
1035
- defaultClaudeBaseDir: path.join(os.homedir(), ".claude")
1051
+ defaultClaudeBaseDir: path.join(os.homedir(), ".claude"),
1052
+ homeDir: os.homedir()
1036
1053
  };
1037
1054
  }
1038
1055
  function readConfig() {
@@ -1748,7 +1765,13 @@ function fold(text) {
1748
1765
  return folded.length === 1 ? folded : character;
1749
1766
  }).toLowerCase();
1750
1767
  }
1768
+ function exactPhrase(query) {
1769
+ const phrase = /^"([^"]+)"$/.exec(query.trim())?.[1].trim();
1770
+ return phrase === void 0 || phrase === "" ? null : phrase;
1771
+ }
1751
1772
  function searchWords(query) {
1773
+ const phrase = exactPhrase(query);
1774
+ if (phrase !== null) return [fold(phrase)];
1752
1775
  return fold(query).replace(/^["'`]+|["'`]+$/g, "").split(/\s+/).filter(Boolean);
1753
1776
  }
1754
1777
  function hasSearchableWord(query) {
@@ -1763,6 +1786,28 @@ function isWordStart(text, index) {
1763
1786
  function isWordEnd(text, end) {
1764
1787
  return end >= text.length || !alphanumeric.test(text[end]) || isWordStart(text, end);
1765
1788
  }
1789
+ function mergeRanges(ranges) {
1790
+ const merged = [];
1791
+ for (const range of [...ranges].sort((a, b) => a.start - b.start)) {
1792
+ const last = merged.at(-1);
1793
+ if (last !== void 0 && range.start <= last.end) last.end = Math.max(last.end, range.end);
1794
+ else merged.push({ ...range });
1795
+ }
1796
+ return merged;
1797
+ }
1798
+ function exactRanges(folded, phrase) {
1799
+ const ranges = [];
1800
+ for (let index = folded.indexOf(phrase); index !== -1; index = folded.indexOf(phrase, index + 1)) {
1801
+ const end = index + phrase.length;
1802
+ const joinedBefore = alphanumeric.test(phrase[0]) && alphanumeric.test(folded[index - 1] ?? "");
1803
+ const joinedAfter = alphanumeric.test(phrase[phrase.length - 1]) && alphanumeric.test(folded[end] ?? "");
1804
+ if (!joinedBefore && !joinedAfter) ranges.push({
1805
+ start: index,
1806
+ end
1807
+ });
1808
+ }
1809
+ return mergeRanges(ranges);
1810
+ }
1766
1811
  function findWord(text, folded, word) {
1767
1812
  let quality = 0;
1768
1813
  let at = -1;
@@ -1782,6 +1827,13 @@ function matchMessage(query, text) {
1782
1827
  const words = searchWords(query);
1783
1828
  if (words.length === 0) return null;
1784
1829
  const folded = fold(text);
1830
+ if (exactPhrase(query) !== null) {
1831
+ const range = exactRanges(folded, words[0]).at(0);
1832
+ return range === void 0 ? null : {
1833
+ quality: 1,
1834
+ offset: range.start
1835
+ };
1836
+ }
1785
1837
  let quality = 1;
1786
1838
  let offset = text.length;
1787
1839
  for (const word of words) {
@@ -2223,7 +2275,10 @@ async function openCodeSessions(paths) {
2223
2275
  directory,
2224
2276
  parentID: null,
2225
2277
  limit: PAGE
2226
- } : { cursor });
2278
+ } : {
2279
+ limit: PAGE,
2280
+ cursor
2281
+ });
2227
2282
  found.push(...page.data);
2228
2283
  cursor = page.data.length === PAGE ? page.cursor.next ?? null : null;
2229
2284
  } while (cursor !== null);
@@ -2360,6 +2415,7 @@ async function allMessages(client, sessionId) {
2360
2415
  order: "asc"
2361
2416
  } : {
2362
2417
  sessionID: sessionId,
2418
+ limit: PAGE,
2363
2419
  cursor
2364
2420
  });
2365
2421
  messages.push(...page.data);
@@ -2903,10 +2959,12 @@ function readProjectFile(cwd, file) {
2903
2959
  content: data.toString("utf8")
2904
2960
  };
2905
2961
  }
2906
- function writeProjectFile(cwd, file, content) {
2962
+ function writeProjectFile(cwd, file, content, base) {
2907
2963
  const full = resolveProjectFile(cwd, file);
2908
2964
  if (!statSync(full).isFile()) throw new Error(`${file} is not a file`);
2965
+ if (base !== null && readFileSync(full, "utf8") !== base) return false;
2909
2966
  writeFileSync(full, content);
2967
+ return true;
2910
2968
  }
2911
2969
  function createProjectEntry(cwd, file, folder) {
2912
2970
  const full = resolveNewProjectPath(cwd, file);
@@ -3172,7 +3230,12 @@ async function gitRepos(cwd) {
3172
3230
  }
3173
3231
  async function commit(cwd, message, andPush) {
3174
3232
  const text = message.trim() === "" ? await commitMessage(cwd, false) : message;
3175
- await git(cwd, ["add", "--all"]);
3233
+ await git(cwd, [
3234
+ "add",
3235
+ "--all",
3236
+ "--",
3237
+ "."
3238
+ ]);
3176
3239
  await git(cwd, [
3177
3240
  "commit",
3178
3241
  "--message",
@@ -3371,7 +3434,9 @@ async function gitStatus(cwd) {
3371
3434
  "--no-optional-locks",
3372
3435
  "status",
3373
3436
  "--porcelain=v2",
3374
- "--branch"
3437
+ "--branch",
3438
+ "--",
3439
+ "."
3375
3440
  ]);
3376
3441
  const status = {
3377
3442
  branch: null,
@@ -3427,14 +3492,16 @@ async function repoDiff(repo, prefix) {
3427
3492
  await diffBase(repo),
3428
3493
  "--no-color",
3429
3494
  "--no-ext-diff",
3495
+ "--relative",
3430
3496
  `--src-prefix=a/${prefix}`,
3431
3497
  `--dst-prefix=b/${prefix}`
3432
3498
  ]);
3433
3499
  const added = (await git(repo, [
3434
3500
  "ls-files",
3435
3501
  "--others",
3436
- "--exclude-standard"
3437
- ])).stdout.split("\n").filter(Boolean).slice(0, UNTRACKED_LIMIT).map((file) => newFilePatch(repo, file, prefix));
3502
+ "--exclude-standard",
3503
+ "-z"
3504
+ ])).stdout.split("\0").filter(Boolean).slice(0, UNTRACKED_LIMIT).map((file) => newFilePatch(repo, file, prefix));
3438
3505
  return [tracked.stdout, ...added].join("");
3439
3506
  }
3440
3507
  async function diffBase(repo) {
@@ -5852,7 +5919,8 @@ async function claudeMessages(sessionId, dir) {
5852
5919
  });
5853
5920
  }
5854
5921
  async function claudeCandidates(query) {
5855
- const words = searchWords(query).filter((word) => word.length >= 3 && !/["\\]/.test(word));
5922
+ const phrase = exactPhrase(query);
5923
+ const words = phrase !== null ? [JSON.stringify(phrase).slice(1, -1)] : searchWords(query).filter((word) => word.length >= 3 && !/["\\]/.test(word));
5856
5924
  if (words.length === 0) return null;
5857
5925
  try {
5858
5926
  const lists = await Promise.all(words.map((word) => sessionsWith(word)));
@@ -5885,7 +5953,7 @@ async function sessionsWith(word) {
5885
5953
  })).stdout.split("\n").filter(Boolean).map((file) => path.basename(file, ".jsonl"));
5886
5954
  }
5887
5955
  async function searchCodex(query, sessions) {
5888
- const longest = [...searchWords(query)].sort((a, b) => b.length - a.length)[0];
5956
+ const longest = exactPhrase(query) ?? [...searchWords(query)].sort((a, b) => b.length - a.length)[0];
5889
5957
  const known = new Map(sessions.map((session) => [session.id, session]));
5890
5958
  const found = (await codexSearch(longest)).filter((result) => known.has(result.thread.id)).sort((a, b) => b.thread.updatedAt - a.thread.updatedAt);
5891
5959
  const verified = await readAndMatch(query, found.slice(0, CODEX_READ_LIMIT).map((result) => ({ id: result.thread.id })), codexMessages);
@@ -6155,7 +6223,7 @@ async function showFile(cwd, ref, file) {
6155
6223
  const output = await git(cwd, [
6156
6224
  "cat-file",
6157
6225
  "blob",
6158
- `${ref}:${file}`
6226
+ `${ref}:./${file}`
6159
6227
  ]);
6160
6228
  if (output.stdout.length > SHOW_LIMIT || output.stdout.includes("\0")) return null;
6161
6229
  return output.stdout;
@@ -6188,6 +6256,7 @@ async function commitFiles(cwd, hash, parent) {
6188
6256
  "--name-status",
6189
6257
  "-M",
6190
6258
  "-z",
6259
+ "--relative",
6191
6260
  parent ?? (await git(cwd, [
6192
6261
  "hash-object",
6193
6262
  "-t",
@@ -6271,7 +6340,7 @@ async function runAction(cwd, action) {
6271
6340
  await git(cwd, [
6272
6341
  "update-index",
6273
6342
  "--cacheinfo",
6274
- `${entry.stdout.split(" ")[0]},${hash},${action.path}`
6343
+ `${entry.stdout.split(" ")[0]},${hash},${await repoPrefix(cwd)}${action.path}`
6275
6344
  ]);
6276
6345
  return;
6277
6346
  }
@@ -6311,7 +6380,12 @@ async function runAction(cwd, action) {
6311
6380
  return;
6312
6381
  }
6313
6382
  if (action.kind === "commit") {
6314
- if (action.all) await git(cwd, ["add", "--all"]);
6383
+ if (action.all) await git(cwd, [
6384
+ "add",
6385
+ "--all",
6386
+ "--",
6387
+ "."
6388
+ ]);
6315
6389
  await git(cwd, [
6316
6390
  "commit",
6317
6391
  ...action.amend ? ["--amend"] : [],
@@ -6365,6 +6439,9 @@ async function network(cwd, args) {
6365
6439
  throw gitError(error);
6366
6440
  }
6367
6441
  }
6442
+ async function repoPrefix(cwd) {
6443
+ return (await git(cwd, ["rev-parse", "--show-prefix"])).stdout.trim();
6444
+ }
6368
6445
  async function repoChanges(cwd) {
6369
6446
  const output = await git(cwd, [
6370
6447
  "--no-optional-locks",
@@ -6372,8 +6449,11 @@ async function repoChanges(cwd) {
6372
6449
  "--porcelain=v2",
6373
6450
  "--branch",
6374
6451
  "--untracked-files=all",
6375
- "-z"
6452
+ "-z",
6453
+ "--",
6454
+ "."
6376
6455
  ]);
6456
+ const prefix = await repoPrefix(cwd);
6377
6457
  const repo = {
6378
6458
  head: null,
6379
6459
  branch: null,
@@ -6396,19 +6476,19 @@ async function repoChanges(cwd) {
6396
6476
  repo.behind = -Number(fields[3]);
6397
6477
  }
6398
6478
  if (fields[0] === "?") repo.changes.push({
6399
- path: tokens[index].slice(2),
6479
+ path: tokens[index].slice(2 + prefix.length),
6400
6480
  from: null,
6401
6481
  status: "U"
6402
6482
  });
6403
6483
  if (fields[0] === "u") repo.merge.push({
6404
- path: fields.slice(10).join(" "),
6484
+ path: fields.slice(10).join(" ").slice(prefix.length),
6405
6485
  from: null,
6406
6486
  status: "!"
6407
6487
  });
6408
6488
  if (fields[0] === "1" || fields[0] === "2") {
6409
6489
  const renamed = fields[0] === "2";
6410
- const file = fields.slice(renamed ? 9 : 8).join(" ");
6411
- const from = renamed ? tokens[index + 1] : null;
6490
+ const file = fields.slice(renamed ? 9 : 8).join(" ").slice(prefix.length);
6491
+ const from = renamed ? tokens[index + 1].slice(prefix.length) : null;
6412
6492
  if (fields[1][0] !== ".") repo.staged.push({
6413
6493
  path: file,
6414
6494
  from,
@@ -7518,10 +7598,7 @@ const handlers = {
7518
7598
  "files.list": (params) => listFiles(projectPath(params.projectId)),
7519
7599
  "files.entries": (params) => listEntries(projectPath(params.projectId)),
7520
7600
  "files.read": (params) => readProjectFile(projectPath(params.projectId), params.path),
7521
- "files.write": (params) => {
7522
- writeProjectFile(projectPath(params.projectId), params.path, params.content);
7523
- return null;
7524
- },
7601
+ "files.write": (params) => writeProjectFile(projectPath(params.projectId), params.path, params.content, params.base),
7525
7602
  "files.create": (params) => {
7526
7603
  createProjectEntry(projectPath(params.projectId), params.path, params.folder);
7527
7604
  return null;
@@ -8626,6 +8703,11 @@ async function forward(site, target, request, response) {
8626
8703
  const kind = rewriteKind(incoming, request);
8627
8704
  response.statusCode = status;
8628
8705
  copyHeaders(incoming, response, kind !== null);
8706
+ if (kind === "html") {
8707
+ response.removeHeader("last-modified");
8708
+ response.removeHeader("expires");
8709
+ response.setHeader("cache-control", "no-store, no-transform");
8710
+ }
8629
8711
  const location = incoming.headers.location;
8630
8712
  if (location !== void 0) {
8631
8713
  const next = URL.parse(location, target);