@cnwenf/occ 2.1.312 → 2.1.314

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.
Files changed (2) hide show
  1. package/dist/cli.js +182 -17
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- globalThis.MACRO={"VERSION":"2.1.312","BINARY_NAME":"occ","BUILD_TIME":"2026-08-25T20:28:13.846Z","FEEDBACK_CHANNEL":"","ISSUES_EXPLAINER":"","NATIVE_PACKAGE_URL":"","PACKAGE_URL":"@cnwenf/occ","VERSION_CHANGELOG":""};
2
+ globalThis.MACRO={"VERSION":"2.1.314","BINARY_NAME":"occ","BUILD_TIME":"2026-08-28T00:45:59.205Z","FEEDBACK_CHANNEL":"","ISSUES_EXPLAINER":"","NATIVE_PACKAGE_URL":"","PACKAGE_URL":"@cnwenf/occ","VERSION_CHANGELOG":""};
3
3
  // @bun
4
4
  var __create = Object.create;
5
5
  var __getProtoOf = Object.getPrototypeOf;
@@ -59313,7 +59313,39 @@ function hasUnescapedEmptyParens(str) {
59313
59313
  }
59314
59314
  return false;
59315
59315
  }
59316
- function validatePermissionRule(rule) {
59316
+ function isOperatorToken(token) {
59317
+ return OPERATOR_TOKEN_RE.test(token);
59318
+ }
59319
+ function hasUnescapedStar(str) {
59320
+ for (let i2 = 0;i2 < str.length; i2++) {
59321
+ if (str[i2] === "*" && !isEscaped(str, i2))
59322
+ return true;
59323
+ }
59324
+ return false;
59325
+ }
59326
+ function wildcardBeforeSubcommand(content) {
59327
+ if (content.endsWith(":*"))
59328
+ return;
59329
+ const tokens = content.trim().split(/\s+/).filter(Boolean);
59330
+ const first = tokens[0];
59331
+ if (tokens.length < 3 || first === undefined || hasUnescapedStar(first)) {
59332
+ return;
59333
+ }
59334
+ let sawWildcard = false;
59335
+ for (const token of tokens.slice(1)) {
59336
+ if (isOperatorToken(token))
59337
+ return;
59338
+ if (hasUnescapedStar(token)) {
59339
+ sawWildcard = true;
59340
+ continue;
59341
+ }
59342
+ if (token.startsWith("-"))
59343
+ continue;
59344
+ return sawWildcard ? first : undefined;
59345
+ }
59346
+ return;
59347
+ }
59348
+ function validatePermissionRule(rule, behavior) {
59317
59349
  if (!rule || rule.trim() === "") {
59318
59350
  return { valid: false, error: "Permission rule cannot be empty" };
59319
59351
  }
@@ -59397,6 +59429,18 @@ function validatePermissionRule(rule) {
59397
59429
  examples: ["Bash(npm:*)", "Bash(git:*)"]
59398
59430
  };
59399
59431
  }
59432
+ if (behavior === "allow") {
59433
+ const flaggedCommand = wildcardBeforeSubcommand(content);
59434
+ if (flaggedCommand !== undefined) {
59435
+ const isGit = flaggedCommand === "git";
59436
+ const gitNote = isGit ? " For git, options such as -c and --exec-path can run arbitrary commands." : "";
59437
+ const gitExample = isGit ? " (for example Bash(git status *))" : "";
59438
+ return {
59439
+ valid: true,
59440
+ warning: `${permissionRuleValueToString(parsed)} has a wildcard before the rest of the command, so it also matches any options inserted at that position and approves them without a prompt.${gitNote} Replace that * with the exact value you mean, or only use * after the subcommand${gitExample}.`
59441
+ };
59442
+ }
59443
+ }
59400
59444
  }
59401
59445
  if (isFilePatternTool(parsed.toolName) && parsed.ruleContent !== undefined) {
59402
59446
  const content = parsed.ruleContent;
@@ -59436,13 +59480,14 @@ function validatePermissionRule(rule) {
59436
59480
  }
59437
59481
  return { valid: true };
59438
59482
  }
59439
- var PermissionRuleSchema;
59483
+ var OPERATOR_TOKEN_RE, PermissionRuleSchema;
59440
59484
  var init_permissionValidation = __esm(() => {
59441
59485
  init_v4();
59442
59486
  init_mcpStringUtils();
59443
59487
  init_permissionRuleParser();
59444
59488
  init_stringUtils();
59445
59489
  init_toolValidationConfig();
59490
+ OPERATOR_TOKEN_RE = /^(?:[|&;<>]|\d+[<>])/;
59446
59491
  PermissionRuleSchema = lazySchema(() => exports_external.string().superRefine((val, ctx) => {
59447
59492
  const result = validatePermissionRule(val);
59448
59493
  if (!result.valid) {
@@ -252307,7 +252352,8 @@ var init_TaskOutput = __esm(() => {
252307
252352
  const tail = safeJoinLines(recent, `
252308
252353
  `);
252309
252354
  const sizeKB = Math.round(this.#totalBytes / 1024);
252310
- const notice = `
252355
+ const notice = this.#disk.failing || this.#disk.lostOutput ? `
252356
+ Output truncated (${sizeKB}KB total). The full output could not all be saved to ${this.path}; that file may be missing or incomplete.` : `
252311
252357
  Output truncated (${sizeKB}KB total). Full output saved to: ${this.path}`;
252312
252358
  return tail ? tail + notice : notice.trimStart();
252313
252359
  }
@@ -385289,6 +385335,35 @@ function hasQuotedBracketCloserInConditional(command4) {
385289
385335
  }
385290
385336
  return false;
385291
385337
  }
385338
+ function hasDanglingBooleanOperator(command4) {
385339
+ if (!command4.includes("&&") && !command4.includes("||"))
385340
+ return false;
385341
+ let inSingle = false;
385342
+ let inDouble = false;
385343
+ let lastBoolOpEnd = -1;
385344
+ for (let i6 = 0;i6 < command4.length; i6++) {
385345
+ const ch2 = command4[i6];
385346
+ if (ch2 === "\\" && !inSingle) {
385347
+ i6++;
385348
+ continue;
385349
+ }
385350
+ if (ch2 === "'" && !inDouble) {
385351
+ inSingle = !inSingle;
385352
+ continue;
385353
+ }
385354
+ if (ch2 === '"' && !inSingle) {
385355
+ inDouble = !inDouble;
385356
+ continue;
385357
+ }
385358
+ if (inSingle || inDouble)
385359
+ continue;
385360
+ if (ch2 === "&" && command4[i6 + 1] === "&" || ch2 === "|" && command4[i6 + 1] === "|") {
385361
+ lastBoolOpEnd = i6 + 2;
385362
+ i6++;
385363
+ }
385364
+ }
385365
+ return lastBoolOpEnd !== -1 && command4.slice(lastBoolOpEnd).trim() === "";
385366
+ }
385292
385367
  async function bashToolHasPermission(input, context4, getCommandSubcommandPrefixFn = getCommandSubcommandPrefix) {
385293
385368
  let appState = context4.getAppState();
385294
385369
  {
@@ -385388,6 +385463,15 @@ async function bashToolHasPermission(input, context4, getCommandSubcommandPrefix
385388
385463
  };
385389
385464
  }
385390
385465
  }
385466
+ {
385467
+ const mode = appState.toolPermissionContext.mode;
385468
+ if (mode !== "bypassPermissions" && hasDanglingBooleanOperator(input.command)) {
385469
+ return {
385470
+ behavior: "ask",
385471
+ message: "Malformed command with a dangling && or || operator requires confirmation."
385472
+ };
385473
+ }
385474
+ }
385391
385475
  const injectionCheckDisabled = isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_COMMAND_INJECTION_CHECK);
385392
385476
  const shadowEnabled = feature("TREE_SITTER_BASH_SHADOW") ? getFeatureValue_CACHED_MAY_BE_STALE("tengu_birch_trellis", true) : false;
385393
385477
  let astRoot = injectionCheckDisabled ? null : feature("TREE_SITTER_BASH_SHADOW") && !shadowEnabled ? null : await parseCommandRaw(input.command);
@@ -635547,19 +635631,19 @@ async function initializeToolPermissionContext({
635547
635631
  }
635548
635632
  for (const rule of rulesFromDisk) {
635549
635633
  const ruleStr = permissionRuleValueToString(rule.ruleValue);
635550
- const result = validatePermissionRule(ruleStr);
635634
+ const result = validatePermissionRule(ruleStr, rule.ruleBehavior);
635551
635635
  if (result.valid && result.warning) {
635552
635636
  warnings.push(`Permission ${rule.ruleBehavior} rule (${formatPermissionSource(rule.source)}): ${result.warning}`);
635553
635637
  }
635554
635638
  }
635555
635639
  for (const ruleStr of parsedAllowedToolsCli) {
635556
- const result = validatePermissionRule(ruleStr);
635640
+ const result = validatePermissionRule(ruleStr, "allow");
635557
635641
  if (result.valid && result.warning) {
635558
635642
  warnings.push(`Permission allow rule (--allowed-tools): ${result.warning}`);
635559
635643
  }
635560
635644
  }
635561
635645
  for (const ruleStr of parsedDisallowedToolsCli) {
635562
- const result = validatePermissionRule(ruleStr);
635646
+ const result = validatePermissionRule(ruleStr, "deny");
635563
635647
  if (result.valid && result.warning) {
635564
635648
  warnings.push(`Permission deny rule (--disallowed-tools): ${result.warning}`);
635565
635649
  }
@@ -726009,6 +726093,7 @@ __export(exports_diskOutput, {
726009
726093
  appendTaskOutput: () => appendTaskOutput,
726010
726094
  _resetTaskOutputDirForTest: () => _resetTaskOutputDirForTest,
726011
726095
  _clearOutputsForTest: () => _clearOutputsForTest,
726096
+ OUTPUT_OMITTED_MARKER_FOR_TEST: () => OUTPUT_OMITTED_MARKER_FOR_TEST,
726012
726097
  MAX_TASK_OUTPUT_BYTES_DISPLAY: () => MAX_TASK_OUTPUT_BYTES_DISPLAY,
726013
726098
  MAX_TASK_OUTPUT_BYTES: () => MAX_TASK_OUTPUT_BYTES,
726014
726099
  DiskTaskOutput: () => DiskTaskOutput
@@ -726049,6 +726134,11 @@ class DiskTaskOutput {
726049
726134
  #queue = [];
726050
726135
  #bytesWritten = 0;
726051
726136
  #capped = false;
726137
+ #unwrittenChars = 0;
726138
+ #cancelCount = 0;
726139
+ #failing = false;
726140
+ #seenFailureKeys = new Set;
726141
+ #lostOutput = false;
726052
726142
  #flushPromise = null;
726053
726143
  #flushResolve = null;
726054
726144
  constructor(taskId) {
@@ -726061,11 +726151,14 @@ class DiskTaskOutput {
726061
726151
  this.#bytesWritten += content.length;
726062
726152
  if (this.#bytesWritten > MAX_TASK_OUTPUT_BYTES) {
726063
726153
  this.#capped = true;
726064
- this.#queue.push(`
726154
+ const marker = `
726065
726155
  [output truncated: exceeded ${MAX_TASK_OUTPUT_BYTES_DISPLAY} disk cap]
726066
- `);
726156
+ `;
726157
+ this.#queue.push(marker);
726158
+ this.#unwrittenChars += marker.length;
726067
726159
  } else {
726068
726160
  this.#queue.push(content);
726161
+ this.#unwrittenChars += content.length;
726069
726162
  }
726070
726163
  if (!this.#flushPromise) {
726071
726164
  this.#flushPromise = new Promise((resolve59) => {
@@ -726077,8 +726170,19 @@ class DiskTaskOutput {
726077
726170
  flush() {
726078
726171
  return this.#flushPromise ?? Promise.resolve();
726079
726172
  }
726173
+ get failing() {
726174
+ return this.#failing;
726175
+ }
726176
+ get lostOutput() {
726177
+ return this.#lostOutput;
726178
+ }
726179
+ get unwrittenChars() {
726180
+ return this.#unwrittenChars;
726181
+ }
726080
726182
  cancel() {
726183
+ this.#cancelCount += 1;
726081
726184
  this.#queue.length = 0;
726185
+ this.#unwrittenChars = 0;
726082
726186
  }
726083
726187
  async#drainAllChunks() {
726084
726188
  while (true) {
@@ -726088,7 +726192,17 @@ class DiskTaskOutput {
726088
726192
  this.#fileHandle = await open15(this.#path, process.platform === "win32" ? "a" : fsConstants9.O_WRONLY | fsConstants9.O_APPEND | fsConstants9.O_CREAT | O_NOFOLLOW2);
726089
726193
  }
726090
726194
  while (true) {
726091
- await this.#writeAllChunks();
726195
+ const cancelCount = this.#cancelCount;
726196
+ try {
726197
+ await this.#writeAllChunks();
726198
+ } catch (e4) {
726199
+ if (this.#cancelCount === cancelCount) {
726200
+ this.#lostOutput = true;
726201
+ this.#queue.unshift(OUTPUT_OMITTED_MARKER);
726202
+ this.#unwrittenChars += OUTPUT_OMITTED_MARKER.length;
726203
+ }
726204
+ throw e4;
726205
+ }
726092
726206
  if (this.#queue.length === 0) {
726093
726207
  break;
726094
726208
  }
@@ -726111,6 +726225,7 @@ class DiskTaskOutput {
726111
726225
  }
726112
726226
  #queueToBuffers() {
726113
726227
  const queue2 = this.#queue.splice(0, this.#queue.length);
726228
+ this.#unwrittenChars = 0;
726114
726229
  let totalLength = 0;
726115
726230
  for (const str2 of queue2) {
726116
726231
  totalLength += Buffer.byteLength(str2, "utf8");
@@ -726125,13 +726240,18 @@ class DiskTaskOutput {
726125
726240
  async#drain() {
726126
726241
  try {
726127
726242
  await this.#drainAllChunks();
726243
+ this.#clearFailureState();
726128
726244
  } catch (e4) {
726129
- logError2(e4);
726245
+ if (!this.#failing) {
726246
+ this.#failing = true;
726247
+ logError2(new Error(`Task output drain failed (will retry once): ${e4}`));
726248
+ }
726130
726249
  if (this.#queue.length > 0) {
726131
726250
  try {
726132
726251
  await this.#drainAllChunks();
726252
+ this.#clearFailureState();
726133
726253
  } catch (e22) {
726134
- logError2(e22);
726254
+ this.#handleFinalDrainFailure(e22);
726135
726255
  }
726136
726256
  }
726137
726257
  } finally {
@@ -726141,6 +726261,30 @@ class DiskTaskOutput {
726141
726261
  resolve59();
726142
726262
  }
726143
726263
  }
726264
+ #clearFailureState() {
726265
+ this.#failing = false;
726266
+ this.#seenFailureKeys.clear();
726267
+ }
726268
+ #handleFinalDrainFailure(e4) {
726269
+ const code = getErrnoCode(e4);
726270
+ const kind = code !== undefined && DISK_EXHAUSTION_ERRNOS.has(code) ? "exhaustion" : "unexpected";
726271
+ const failureKey = `${kind}:${code ?? "no errno"}`;
726272
+ if (!this.#seenFailureKeys.has(failureKey)) {
726273
+ this.#seenFailureKeys.add(failureKey);
726274
+ if (kind === "exhaustion") {
726275
+ logError2(new Error(`Task output drain retry failed (${code}): ${e4}`));
726276
+ } else {
726277
+ logError2(e4);
726278
+ }
726279
+ }
726280
+ if (this.#unwrittenChars > MAX_UNWRITTEN_CHARS_BEFORE_DROP) {
726281
+ logError2(new Error(`Task output still cannot be written (${code ?? "no errno"}); dropped ${this.#unwrittenChars} chars of unwritten output`));
726282
+ this.#lostOutput = true;
726283
+ this.#queue.length = 0;
726284
+ this.#queue.push(OUTPUT_OMITTED_MARKER);
726285
+ this.#unwrittenChars = OUTPUT_OMITTED_MARKER.length;
726286
+ }
726287
+ }
726144
726288
  }
726145
726289
  async function _clearOutputsForTest() {
726146
726290
  for (const output2 of outputs.values()) {
@@ -726159,6 +726303,14 @@ function getOrCreateOutput(taskId) {
726159
726303
  }
726160
726304
  return output2;
726161
726305
  }
726306
+ function logTaskOutputFailure(context8, e4) {
726307
+ const code = getErrnoCode(e4);
726308
+ if (code && DISK_EXHAUSTION_ERRNOS.has(code)) {
726309
+ logError2(new Error(`${context8} failed (${code}): ${e4}`));
726310
+ } else {
726311
+ logError2(e4);
726312
+ }
726313
+ }
726162
726314
  function appendTaskOutput(taskId, content) {
726163
726315
  getOrCreateOutput(taskId).append(content);
726164
726316
  }
@@ -726173,6 +726325,9 @@ function evictTaskOutput(taskId) {
726173
726325
  const output2 = outputs.get(taskId);
726174
726326
  if (output2) {
726175
726327
  await output2.flush();
726328
+ if (output2.failing && output2.unwrittenChars > 0) {
726329
+ logError2(new Error(`Task output writer evicted while failing; discarded ${output2.unwrittenChars} chars of unwritten output`));
726330
+ }
726176
726331
  outputs.delete(taskId);
726177
726332
  }
726178
726333
  })());
@@ -726192,7 +726347,7 @@ async function getTaskOutputDelta(taskId, fromOffset, maxBytes = DEFAULT_MAX_REA
726192
726347
  if (code === "ENOENT") {
726193
726348
  return { content: "", newOffset: fromOffset };
726194
726349
  }
726195
- logError2(e4);
726350
+ logTaskOutputFailure("getTaskOutputDelta", e4);
726196
726351
  return { content: "", newOffset: fromOffset };
726197
726352
  }
726198
726353
  }
@@ -726209,7 +726364,7 @@ ${content}`;
726209
726364
  if (code === "ENOENT") {
726210
726365
  return "";
726211
726366
  }
726212
- logError2(e4);
726367
+ logTaskOutputFailure("getTaskOutput", e4);
726213
726368
  return "";
726214
726369
  }
726215
726370
  }
@@ -726221,7 +726376,7 @@ async function getTaskOutputSize(taskId) {
726221
726376
  if (code === "ENOENT") {
726222
726377
  return 0;
726223
726378
  }
726224
- logError2(e4);
726379
+ logTaskOutputFailure("getTaskOutputSize", e4);
726225
726380
  return 0;
726226
726381
  }
726227
726382
  }
@@ -726238,7 +726393,7 @@ async function cleanupTaskOutput(taskId) {
726238
726393
  if (code === "ENOENT") {
726239
726394
  return;
726240
726395
  }
726241
- logError2(e4);
726396
+ logTaskOutputFailure("cleanupTaskOutput", e4);
726242
726397
  }
726243
726398
  }
726244
726399
  function initTaskOutput(taskId) {
@@ -726268,7 +726423,9 @@ function initTaskOutputAsSymlink(taskId, targetPath) {
726268
726423
  }
726269
726424
  })());
726270
726425
  }
726271
- var O_NOFOLLOW2, DEFAULT_MAX_READ_BYTES, MAX_TASK_OUTPUT_BYTES, MAX_TASK_OUTPUT_BYTES_DISPLAY = "5GB", _taskOutputDir, _pendingOps, outputs;
726426
+ var O_NOFOLLOW2, DEFAULT_MAX_READ_BYTES, MAX_TASK_OUTPUT_BYTES, MAX_TASK_OUTPUT_BYTES_DISPLAY = "5GB", MAX_UNWRITTEN_CHARS_BEFORE_DROP, OUTPUT_OMITTED_MARKER = `
726427
+ [output omitted: it could not be written to disk]
726428
+ `, OUTPUT_OMITTED_MARKER_FOR_TEST, DISK_EXHAUSTION_ERRNOS, _taskOutputDir, _pendingOps, outputs;
726272
726429
  var init_diskOutput = __esm(() => {
726273
726430
  init_state();
726274
726431
  init_errors();
@@ -726278,6 +726435,14 @@ var init_diskOutput = __esm(() => {
726278
726435
  O_NOFOLLOW2 = fsConstants9.O_NOFOLLOW ?? 0;
726279
726436
  DEFAULT_MAX_READ_BYTES = 8 * 1024 * 1024;
726280
726437
  MAX_TASK_OUTPUT_BYTES = 5 * 1024 * 1024 * 1024;
726438
+ MAX_UNWRITTEN_CHARS_BEFORE_DROP = 16 * 1024 * 1024;
726439
+ OUTPUT_OMITTED_MARKER_FOR_TEST = OUTPUT_OMITTED_MARKER;
726440
+ DISK_EXHAUSTION_ERRNOS = new Set([
726441
+ "ENOSPC",
726442
+ "EDQUOT",
726443
+ "ENFILE",
726444
+ "EMFILE"
726445
+ ]);
726281
726446
  _pendingOps = new Set;
726282
726447
  outputs = new Map;
726283
726448
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cnwenf/occ",
3
- "version": "2.1.312",
3
+ "version": "2.1.314",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "bin": {