@swifty.js/swifty 0.0.27 → 0.0.29

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 (51) hide show
  1. package/README.md +47 -37
  2. package/dist/agent-YR6YK26C.js +4 -0
  3. package/dist/anthropic-5GRR5JCT.js +4 -0
  4. package/dist/checker-N5TIJEN5.js +4 -0
  5. package/dist/chunk-D34FUVGU.js +4 -0
  6. package/dist/{chunk-UGJVMFGH.js → chunk-K5ZK27BX.js} +1 -1
  7. package/dist/chunk-LIMEBKCY.js +408 -0
  8. package/dist/chunk-OTVZGPHD.js +121 -0
  9. package/dist/chunk-POHIQUFP.js +301 -0
  10. package/dist/chunk-QCKJLO6X.js +4 -0
  11. package/dist/chunk-QFSCPD63.js +4 -0
  12. package/dist/chunk-TMEX7RFA.js +4 -0
  13. package/dist/chunk-Y6SQB5FG.js +4 -0
  14. package/dist/lib/agent-5WEKVRTD.js +11 -0
  15. package/dist/lib/{anthropic-RYPJDHQM.js → anthropic-KWO3KLNV.js} +4 -5
  16. package/dist/lib/{checker-6BW4222R.js → checker-AXHPKVBY.js} +2 -2
  17. package/dist/lib/{chunk-HK2Z6WP4.js → chunk-2IVEVG5N.js} +5 -1
  18. package/dist/lib/{chunk-XFSN4LMA.js → chunk-3FBS5NB7.js} +71 -18
  19. package/dist/lib/{chunk-GNI7YX6F.js → chunk-DJ6AILPN.js} +70 -24
  20. package/dist/lib/chunk-GCY44T7S.js +2346 -0
  21. package/dist/lib/{chunk-PZ42NAFA.js → chunk-KG4MJGKQ.js} +19 -21
  22. package/dist/lib/{chunk-2AUSNVIB.js → chunk-KZSBR4FO.js} +44 -37
  23. package/dist/lib/chunk-PIL7M52F.js +127 -0
  24. package/dist/lib/chunk-Z4CYHTII.js +392 -0
  25. package/dist/lib/index.d.ts +227 -132
  26. package/dist/lib/index.js +2195 -1602
  27. package/dist/lib/{openai-VX5VBXZ4.js → openai-PBF7EWNJ.js} +2 -3
  28. package/dist/lib/{tool-filter-VF7TZRE5.js → tool-filter-R6HDDJHE.js} +1 -1
  29. package/dist/main.js +203 -194
  30. package/dist/{openai-5FDSLJNM.js → openai-QQ2K7GPF.js} +14 -14
  31. package/dist/{server-F666APPN.js → server-G23HCFLI.js} +21 -21
  32. package/dist/{tool-filter-NO7I3HFD.js → tool-filter-VBP6WOGO.js} +1 -1
  33. package/package.json +2 -2
  34. package/dist/agent-OMC6YIMW.js +0 -4
  35. package/dist/anthropic-T5K4BQPB.js +0 -4
  36. package/dist/checker-TOQVEG3P.js +0 -4
  37. package/dist/chunk-5I2WXSGV.js +0 -90
  38. package/dist/chunk-A2VR3BC4.js +0 -4
  39. package/dist/chunk-A57FEYSN.js +0 -4
  40. package/dist/chunk-BRC5L644.js +0 -130
  41. package/dist/chunk-FZPTNGTU.js +0 -4
  42. package/dist/chunk-G6YIQW3Z.js +0 -238
  43. package/dist/chunk-I2XG2PXV.js +0 -4
  44. package/dist/chunk-LVSHXIBK.js +0 -389
  45. package/dist/chunk-SULWNDNC.js +0 -4
  46. package/dist/chunk-WO6DL7OB.js +0 -35
  47. package/dist/lib/agent-U7MIUCKT.js +0 -9
  48. package/dist/lib/chunk-2QILP24G.js +0 -1120
  49. package/dist/lib/chunk-3LU4APFB.js +0 -339
  50. package/dist/lib/chunk-OO2CLOEE.js +0 -88
  51. package/dist/lib/chunk-RKJYTYQM.js +0 -1285
@@ -1,15 +1,15 @@
1
- import {
2
- getMaxOutputTokens,
3
- resolveAPIKey
4
- } from "./chunk-3LU4APFB.js";
5
1
  import {
6
2
  AuthenticationError,
7
3
  ContextTooLongError,
8
4
  LLMError,
9
5
  NetworkError,
10
6
  RateLimitError,
11
- ensureToolPairing
12
- } from "./chunk-OO2CLOEE.js";
7
+ ensureToolPairing,
8
+ getMaxOutputTokens,
9
+ getThinkingLevel,
10
+ resolveAPIKey,
11
+ toReasoningEffort
12
+ } from "./chunk-Z4CYHTII.js";
13
13
  import {
14
14
  asRecord,
15
15
  asString,
@@ -27,11 +27,12 @@ var OpenAIClient = class {
27
27
  model;
28
28
  systemPrompt;
29
29
  maxOutputTokens;
30
+ thinkingLevel;
30
31
  constructor(config, systemPrompt) {
31
32
  const apiKey = resolveAPIKey(config);
32
33
  if (!apiKey) {
33
34
  throw new AuthenticationError(
34
- "OpenAI API key not found, set OPENAI_API_KEY in .swifty/config.y(a)ml, or via OPENAI_API_KEY env variable."
35
+ "OpenAI API key not found, set OPENAI_API_KEY in ~/.swifty/config.yaml, or via OPENAI_API_KEY env variable."
35
36
  );
36
37
  }
37
38
  this.client = new OpenAI({
@@ -41,6 +42,7 @@ var OpenAIClient = class {
41
42
  this.model = config.model;
42
43
  this.systemPrompt = systemPrompt;
43
44
  this.maxOutputTokens = getMaxOutputTokens(config);
45
+ this.thinkingLevel = getThinkingLevel(config);
44
46
  }
45
47
  async *stream(conversation, toolSchemas, abortSignal) {
46
48
  const messages = buildOpenAIInput(ensureToolPairing(conversation.getMessages()));
@@ -67,7 +69,10 @@ var OpenAIClient = class {
67
69
  input,
68
70
  stream: true,
69
71
  max_output_tokens: this.maxOutputTokens,
70
- ...tools.length > 0 ? { tools } : {}
72
+ ...tools.length > 0 ? { tools } : {},
73
+ // PI-equivalent thinking level → Responses API reasoning effort. Omitted
74
+ // entirely when thinking is off so non-reasoning models are unaffected.
75
+ ...this.thinkingLevel !== "off" ? { reasoning: { effort: toReasoningEffort(this.thinkingLevel), summary: "auto" } } : {}
71
76
  };
72
77
  let inputTokens = 0;
73
78
  let outputTokens = 0;
@@ -82,6 +87,7 @@ var OpenAIClient = class {
82
87
  let jsonAccumulate = "";
83
88
  let reasoningId = "";
84
89
  let reasoningText = "";
90
+ let sawTerminalResponse = false;
85
91
  for await (const event of stream) {
86
92
  if (event.type === "response.output_text.delta") {
87
93
  yield {
@@ -139,19 +145,22 @@ var OpenAIClient = class {
139
145
  currentToolId = "";
140
146
  jsonAccumulate = "";
141
147
  }
142
- } else if (event.type === "response.completed") {
148
+ } else if (event.type === "response.completed" || event.type === "response.incomplete") {
149
+ sawTerminalResponse = true;
143
150
  const usage = event.response.usage;
144
151
  if (usage) {
145
152
  outputTokens = usage.output_tokens;
146
- cacheReadInputTokens = usage.input_tokens_details.cached_tokens;
153
+ cacheReadInputTokens = usage.input_tokens_details?.cached_tokens ?? 0;
147
154
  inputTokens = Math.max(0, usage.input_tokens - cacheReadInputTokens);
148
155
  }
149
156
  let stopReason = "end_turn";
150
157
  const resp = event.response;
151
- if (resp.status === "incomplete") {
158
+ if (event.type === "response.incomplete" || resp.status === "incomplete") {
152
159
  const details = resp.incomplete_details;
153
160
  if (details?.reason === "max_output_tokens") {
154
161
  stopReason = "max_tokens";
162
+ } else {
163
+ throw new LLMError(`Response incomplete: ${details?.reason ?? "unknown reason"}`);
155
164
  }
156
165
  }
157
166
  yield {
@@ -165,8 +174,18 @@ var OpenAIClient = class {
165
174
  // 0
166
175
  }
167
176
  };
177
+ } else if (event.type === "response.failed") {
178
+ const error = event.response.error;
179
+ const message = `${error?.code ?? "unknown"}: ${error?.message ?? "Response failed"}`;
180
+ throw containsContextLengthError(message) ? new ContextTooLongError(message) : new LLMError(message);
181
+ } else if (event.type === "error") {
182
+ const message = `${event.code ?? "unknown"}: ${event.message}`;
183
+ throw containsContextLengthError(message) ? new ContextTooLongError(message) : new LLMError(message);
168
184
  }
169
185
  }
186
+ if (!sawTerminalResponse) {
187
+ throw new NetworkError("OpenAI Responses stream ended before a terminal response event");
188
+ }
170
189
  } catch (err) {
171
190
  log.error({ err }, "llm operation failed");
172
191
  throw classifyOpenAIError(err);
@@ -178,6 +197,12 @@ var OpenAIClient = class {
178
197
  setMaxOutputTokens(maxTokens) {
179
198
  this.maxOutputTokens = maxTokens;
180
199
  }
200
+ setThinkingLevel(level) {
201
+ this.thinkingLevel = level;
202
+ }
203
+ getThinkingLevel() {
204
+ return this.thinkingLevel;
205
+ }
181
206
  };
182
207
  function imageDataUrl(block) {
183
208
  if (!isRecord(block) || block.type !== "image" || !isRecord(block.source)) {
@@ -349,6 +374,9 @@ function buildOpenAIInput(messages) {
349
374
  output: toolOutputForResponses(tr)
350
375
  });
351
376
  }
377
+ if (m.content.length > 0) {
378
+ result.push({ role: "user", content: userContentsFor(m.content) });
379
+ }
352
380
  } else if (m.role === "assistant") {
353
381
  result.push({
354
382
  role: "assistant",
@@ -371,17 +399,19 @@ var OpenAICompatClient = class {
371
399
  model;
372
400
  systemPrompt;
373
401
  maxOutputTokens;
402
+ thinkingLevel;
374
403
  constructor(config, systemPrompt) {
375
404
  const apiKey = resolveAPIKey(config);
376
405
  if (!apiKey) {
377
406
  throw new AuthenticationError(
378
- "OpenAI API key not found. Set OPENAI_API_KEY in .swifty/config.y(a)ml, or via OPENAI_API_KEY env variable."
407
+ "OpenAI API key not found. Set OPENAI_API_KEY in ~/.swifty/config.yaml, or via OPENAI_API_KEY env variable."
379
408
  );
380
409
  }
381
410
  this.client = new OpenAI({ apiKey, baseURL: config.base_url });
382
411
  this.model = config.model;
383
412
  this.systemPrompt = systemPrompt;
384
413
  this.maxOutputTokens = getMaxOutputTokens(config);
414
+ this.thinkingLevel = getThinkingLevel(config);
385
415
  }
386
416
  setSystemPrompt(prompt) {
387
417
  this.systemPrompt = prompt;
@@ -389,6 +419,12 @@ var OpenAICompatClient = class {
389
419
  setMaxOutputTokens(maxTokens) {
390
420
  this.maxOutputTokens = maxTokens;
391
421
  }
422
+ setThinkingLevel(level) {
423
+ this.thinkingLevel = level;
424
+ }
425
+ getThinkingLevel() {
426
+ return this.thinkingLevel;
427
+ }
392
428
  async *stream(conversation, toolSchemas, abortSignal) {
393
429
  const messages = [
394
430
  {
@@ -414,7 +450,10 @@ var OpenAICompatClient = class {
414
450
  stream: true,
415
451
  stream_options: { include_usage: true },
416
452
  max_tokens: this.maxOutputTokens,
417
- ...tools.length > 0 ? { tools } : {}
453
+ ...tools.length > 0 ? { tools } : {},
454
+ // PI-equivalent thinking level → Chat Completions reasoning_effort.
455
+ // Omitted when thinking is off so non-reasoning models are unaffected.
456
+ ...this.thinkingLevel !== "off" ? { reasoning_effort: toReasoningEffort(this.thinkingLevel) } : {}
418
457
  };
419
458
  let inputTokens = 0;
420
459
  let outputTokens = 0;
@@ -510,6 +549,9 @@ var OpenAICompatClient = class {
510
549
  }
511
550
  }
512
551
  }
552
+ if (finishReason === null) {
553
+ throw new NetworkError("Chat Completions stream ended before a finish reason");
554
+ }
513
555
  let stopReason;
514
556
  if (finishReason === "length") {
515
557
  stopReason = "max_tokens";
@@ -535,6 +577,9 @@ var OpenAICompatClient = class {
535
577
  }
536
578
  };
537
579
  function classifyOpenAIError(err) {
580
+ if (err instanceof LLMError) {
581
+ return err;
582
+ }
538
583
  if (err instanceof OpenAI.APIError) {
539
584
  if (err.status === 413 /* PromptTooLong */ || err.status === 400 /* BadRequest */ && containsContextLengthError(err.message)) {
540
585
  return new ContextTooLongError(`Context Too Long: ${err.message}`);
@@ -543,7 +588,11 @@ function classifyOpenAIError(err) {
543
588
  return new AuthenticationError(`Invalid API key: ${err.message}`);
544
589
  }
545
590
  if (err.status === 429 /* RateLimitError */) {
546
- return new RateLimitError(`Rate limit error, please wait.`);
591
+ const headers = err.headers;
592
+ return new RateLimitError(
593
+ "Rate limit error, please wait.",
594
+ headers instanceof Headers ? headers.get("retry-after") ?? void 0 : void 0
595
+ );
547
596
  }
548
597
  return new LLMError(`OpenAI API error (${asString(err.status)}): ${err.message}`);
549
598
  }
@@ -558,16 +607,14 @@ function buildChatCompletionMessages(messages) {
558
607
  params.push({
559
608
  role: "assistant",
560
609
  content: assistantText || null,
610
+ ...reasoning ? { reasoning_content: reasoning } : {},
561
611
  tool_calls: m.toolUses.map((tu) => ({
562
612
  id: tu.toolUseId,
563
613
  type: "function",
564
614
  function: {
565
615
  name: tu.toolName,
566
616
  arguments: JSON.stringify(tu.arguments)
567
- },
568
- ...reasoning ? {
569
- reasoning_content: reasoning
570
- } : {}
617
+ }
571
618
  }))
572
619
  });
573
620
  } else if (m.toolResults && m.toolResults.length > 0) {
@@ -580,6 +627,12 @@ function buildChatCompletionMessages(messages) {
580
627
  });
581
628
  pendingRichParts.push(...collectRichParts(tr));
582
629
  }
630
+ if (m.content.length > 0) {
631
+ const content = userPartsFor(m.content);
632
+ pendingRichParts.push(
633
+ ...typeof content === "string" ? [{ type: "text", text: content }] : content
634
+ );
635
+ }
583
636
  if (pendingRichParts.length > 0) {
584
637
  params.push({
585
638
  role: "user",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  mcpCallPermissionContent
3
- } from "./chunk-PZ42NAFA.js";
3
+ } from "./chunk-KG4MJGKQ.js";
4
4
  import {
5
5
  createChildLogger,
6
6
  strArg
@@ -12,9 +12,38 @@ import {
12
12
  // src/permissions/checker.ts
13
13
  import { readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
14
14
  import { homedir, tmpdir } from "os";
15
- import { join, resolve, dirname } from "path";
15
+ import { join as join2, resolve as resolve2, dirname as dirname2 } from "path";
16
16
  import yaml from "js-yaml";
17
17
  import z, { parse } from "zod";
18
+
19
+ // src/utils/paths.ts
20
+ import { realpathSync } from "fs";
21
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "path";
22
+ function canonicalPath(path) {
23
+ let current = resolve(path);
24
+ const missing = [];
25
+ for (; ; ) {
26
+ try {
27
+ return join(realpathSync(current), ...missing);
28
+ } catch (error) {
29
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT" && error.code !== "ENOTDIR") {
30
+ throw error;
31
+ }
32
+ const parent = dirname(current);
33
+ if (parent === current) {
34
+ return resolve(path);
35
+ }
36
+ missing.unshift(basename(current));
37
+ current = parent;
38
+ }
39
+ }
40
+ }
41
+ function isPathWithin(root, path) {
42
+ const child = relative(root, path);
43
+ return child === "" || child !== ".." && !child.startsWith(`..${sep}`) && !isAbsolute(child);
44
+ }
45
+
46
+ // src/permissions/checker.ts
18
47
  var log = createChildLogger({ module: "permissions" });
19
48
  var DANGEROUS_PATTERNS = [];
20
49
  var SAFE_PREFIXES = [
@@ -56,7 +85,8 @@ var CONTENT_FIELDS = {
56
85
  WriteFile: "file_path",
57
86
  EditFile: "file_path",
58
87
  Glob: "pattern",
59
- Grep: "pattern"
88
+ Grep: "pattern",
89
+ InstallSkill: "source"
60
90
  };
61
91
  var DEFAULT_DENY_WRITE = [
62
92
  ".swifty/config.yaml",
@@ -79,25 +109,26 @@ var PathSandbox = class {
79
109
  denyWritePaths;
80
110
  projectDir;
81
111
  constructor(projectDir) {
82
- this.projectDir = resolve(projectDir);
112
+ this.projectDir = resolve2(projectDir);
83
113
  this.allowedRoots = [this.projectDir, tmpdir()];
84
- this.denyWritePaths = DEFAULT_DENY_WRITE.map((p) => join(this.projectDir, p));
114
+ this.denyWritePaths = DEFAULT_DENY_WRITE.map((p) => join2(this.projectDir, p));
85
115
  }
86
116
  addRoot(root) {
87
- this.allowedRoots.push(resolve(root));
117
+ this.allowedRoots.push(resolve2(root));
88
118
  }
89
119
  // Add custom deny-write paths
90
120
  addDenyWrite(path) {
91
- this.denyWritePaths.push(resolve(path));
121
+ this.denyWritePaths.push(resolve2(path));
92
122
  }
93
123
  /**
94
124
  * Check whether a path is in the deny-write list.
95
125
  * denyWrite has the highest priority — even if the path is within an allowed root, writes are still denied.
96
126
  */
97
127
  checkDenyWrite(filePath) {
98
- const absolute = resolve(filePath);
128
+ const absolute = resolve2(this.projectDir, filePath);
129
+ const canonical = canonicalPath(absolute);
99
130
  for (const denied of this.denyWritePaths) {
100
- if (absolute.startsWith(denied)) {
131
+ if (isPathWithin(denied, absolute) || isPathWithin(canonicalPath(denied), canonical)) {
101
132
  return {
102
133
  effect: "deny",
103
134
  reason: `Path ${filePath} is in deny-write list`
@@ -107,9 +138,9 @@ var PathSandbox = class {
107
138
  return null;
108
139
  }
109
140
  check(filePath) {
110
- const absolute = resolve(filePath);
141
+ const absolute = canonicalPath(resolve2(this.projectDir, filePath));
111
142
  for (const root of this.allowedRoots) {
112
- if (absolute.startsWith(root)) {
143
+ if (isPathWithin(canonicalPath(root), absolute)) {
113
144
  return null;
114
145
  }
115
146
  }
@@ -190,9 +221,9 @@ var RuleEngine = class {
190
221
  localPath;
191
222
  cache = /* @__PURE__ */ new Map();
192
223
  constructor(workDir) {
193
- this.userPath = join(homedir(), ".swifty", "permissions.yaml");
194
- this.projectPath = join(workDir, ".swifty", "permissions.yaml");
195
- this.localPath = join(workDir, ".swifty", "permissions.local.yaml");
224
+ this.userPath = join2(homedir(), ".swifty", "permissions.yaml");
225
+ this.projectPath = join2(workDir, ".swifty", "permissions.yaml");
226
+ this.localPath = join2(workDir, ".swifty", "permissions.local.yaml");
196
227
  }
197
228
  // Read a single rules file; skips disk I/O and parsing on cache hit.
198
229
  rulesFor(path) {
@@ -229,7 +260,7 @@ var RuleEngine = class {
229
260
  // Persists a rule to the project-local YAML file in the `Tool(pattern)`
230
261
  // format so "allow always" survives a restart.
231
262
  appendLocalRule(rule) {
232
- mkdirSync(dirname(this.localPath), { recursive: true });
263
+ mkdirSync(dirname2(this.localPath), { recursive: true });
233
264
  const rules = loadRulesFile(this.localPath);
234
265
  const exists = rules.some(
235
266
  (r) => r.tool === rule.tool && r.pattern === rule.pattern && r.effect === rule.effect
@@ -255,7 +286,7 @@ function detectDangerous(command) {
255
286
  }
256
287
  function isSafeCommand(command) {
257
288
  const trimmed = command.trim();
258
- if (trimmed.includes(">") || trimmed.includes("|") || trimmed.includes(";") || trimmed.includes("&&") || trimmed.includes("$(") || trimmed.includes("`")) {
289
+ if (trimmed.includes(">") || trimmed.includes("<") || /[\r\n&]/.test(trimmed) || trimmed.includes("|") || trimmed.includes(";") || trimmed.includes("&&") || trimmed.includes("$(") || trimmed.includes("`")) {
259
290
  return false;
260
291
  }
261
292
  return SAFE_PREFIXES.some(
@@ -275,7 +306,14 @@ function modeDecide(mode, category) {
275
306
  return category === "read" ? "allow" : "ask";
276
307
  }
277
308
  }
278
- var PermissionChecker = class {
309
+ var PermissionChecker = class _PermissionChecker {
310
+ constructor(workDir, mode = "default") {
311
+ this.workDir = workDir;
312
+ this.mode = mode;
313
+ this.sandbox = new PathSandbox(workDir);
314
+ this.ruleEngine = new RuleEngine(workDir);
315
+ }
316
+ workDir;
279
317
  mode;
280
318
  planFilePath = "";
281
319
  // Sandbox mode: when enabled, command-category tools run through OS sandbox isolation, with optional auto-allow
@@ -283,18 +321,24 @@ var PermissionChecker = class {
283
321
  sandboxAutoAllow = false;
284
322
  sandbox;
285
323
  ruleEngine;
286
- constructor(workDir, mode = "default") {
287
- this.mode = mode;
288
- this.sandbox = new PathSandbox(workDir);
289
- this.ruleEngine = new RuleEngine(workDir);
324
+ forWorkDir(workDir) {
325
+ const checker = new _PermissionChecker(workDir, this.mode);
326
+ checker.ruleEngine = this.ruleEngine;
327
+ checker.sandboxEnabled = this.sandboxEnabled;
328
+ checker.sandboxAutoAllow = this.sandboxAutoAllow;
329
+ return checker;
290
330
  }
291
331
  check(toolName, category, args) {
292
332
  const content = extractContent(toolName, args);
293
333
  let snapshot = null;
294
334
  const rules = () => snapshot ??= this.ruleEngine.snapshot();
335
+ const explicitEffect = evaluateRules(rules(), toolName, content);
336
+ if (explicitEffect === "deny" || explicitEffect === "ask") {
337
+ return { effect: explicitEffect, reason: `Permission rule: ${explicitEffect}` };
338
+ }
295
339
  if (this.mode === "plan" && (toolName === "WriteFile" || toolName === "EditFile")) {
296
340
  const path = strArg(args, "file_path", "");
297
- if (path.includes(".swifty/plans/")) {
341
+ if (this.planFilePath && canonicalPath(resolve2(this.workDir, path)) === canonicalPath(resolve2(this.workDir, this.planFilePath)) && !this.sandbox.checkDenyWrite(path)) {
298
342
  return {
299
343
  effect: "allow",
300
344
  reason: "Plan file write allowed in plan mode"
@@ -377,13 +421,13 @@ var PermissionChecker = class {
377
421
  const isFilePath = toolName === "ReadFile" || toolName === "WriteFile" || toolName === "EditFile";
378
422
  let pattern;
379
423
  if (isFilePath && content) {
380
- const abs = resolve(content);
424
+ const abs = resolve2(this.workDir, content);
381
425
  let isDir = false;
382
426
  try {
383
427
  isDir = statSync(abs).isDirectory();
384
428
  } catch {
385
429
  }
386
- pattern = join(isDir ? abs : dirname(abs), "*");
430
+ pattern = join2(isDir ? abs : dirname2(abs), "*");
387
431
  } else {
388
432
  const words = content.trim().split(/\s+/).slice(0, 2);
389
433
  pattern = words.join(" ") + "*";
@@ -417,6 +461,8 @@ var PermissionChecker = class {
417
461
  };
418
462
 
419
463
  export {
464
+ canonicalPath,
465
+ isPathWithin,
420
466
  extractContent,
421
467
  PathSandbox,
422
468
  evaluateRules,