@sentry/junior 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -29,7 +29,10 @@ export const runtime = "nodejs";
29
29
 
30
30
  ```js
31
31
  import { withJunior } from "@sentry/junior/config";
32
- export default withJunior();
32
+
33
+ export default withJunior({
34
+ pluginPackages: ["@sentry/junior-github", "@sentry/junior-sentry"]
35
+ });
33
36
  ```
34
37
 
35
38
  ## Full docs
@@ -4,9 +4,10 @@ import {
4
4
  createNormalizingStream,
5
5
  resetBotDepsForTests,
6
6
  setBotDepsForTests
7
- } from "./chunk-RXNMJQPY.js";
8
- import "./chunk-QHDDCUTN.js";
7
+ } from "./chunk-L745IWNK.js";
8
+ import "./chunk-DPTR2FNH.js";
9
9
  import "./chunk-PY4AI2GZ.js";
10
+ import "./chunk-SP6LV35L.js";
10
11
  export {
11
12
  appSlackRuntime,
12
13
  bot,
@@ -4,7 +4,7 @@ import {
4
4
  downloadPrivateSlackFile,
5
5
  getThreadMessageTopic,
6
6
  removeReactionFromMessage
7
- } from "./chunk-RXNMJQPY.js";
7
+ } from "./chunk-L745IWNK.js";
8
8
  import {
9
9
  acquireQueueMessageProcessingOwnership,
10
10
  completeQueueMessageProcessingOwnership,
@@ -12,7 +12,7 @@ import {
12
12
  getQueueMessageProcessingState,
13
13
  getStateAdapter,
14
14
  refreshQueueMessageProcessingOwnership
15
- } from "./chunk-QHDDCUTN.js";
15
+ } from "./chunk-DPTR2FNH.js";
16
16
  import {
17
17
  createRequestContext,
18
18
  logError,
@@ -4,6 +4,10 @@ import {
4
4
  setSpanAttributes,
5
5
  withSpan
6
6
  } from "./chunk-PY4AI2GZ.js";
7
+ import {
8
+ discoverInstalledPluginPackageContent,
9
+ discoverProjectRoots
10
+ } from "./chunk-SP6LV35L.js";
7
11
 
8
12
  // src/chat/config.ts
9
13
  var MIN_AGENT_TURN_TIMEOUT_MS = 10 * 1e3;
@@ -385,15 +389,15 @@ import { createHash } from "crypto";
385
389
  import { Sandbox } from "@vercel/sandbox";
386
390
 
387
391
  // src/chat/plugins/registry.ts
388
- import { readFileSync as readFileSync2, readdirSync, statSync as statSync2 } from "fs";
389
- import path3 from "path";
392
+ import { readFileSync, readdirSync, statSync } from "fs";
393
+ import path2 from "path";
390
394
  import { parse as parseYaml } from "yaml";
391
395
 
392
396
  // src/chat/home.ts
393
397
  import fs from "fs";
394
398
  import path from "path";
395
399
  function homeDir() {
396
- return detectAppDir();
400
+ return resolveHomeDir();
397
401
  }
398
402
  function unique(values) {
399
403
  return [...new Set(values)];
@@ -409,46 +413,60 @@ function pathExists(targetPath) {
409
413
  function hasAnyDataMarkers(appDir) {
410
414
  return pathExists(path.join(appDir, "SOUL.md"));
411
415
  }
412
- function detectAppDir() {
413
- const cwd = process.cwd();
414
- const directApp = path.resolve(cwd, "app");
415
- if (pathExists(directApp)) {
416
- return directApp;
416
+ function scoreAppCandidate(appDir) {
417
+ let score = 0;
418
+ if (pathExists(path.join(appDir, "SOUL.md"))) {
419
+ score += 4;
417
420
  }
418
- const candidates = [];
419
- const packageRoots = [];
420
- const localPackagesDir = path.join(cwd, "packages");
421
- if (pathExists(localPackagesDir)) {
422
- for (const entry of fs.readdirSync(localPackagesDir)) {
423
- packageRoots.push(path.join(localPackagesDir, entry));
424
- }
421
+ if (pathExists(path.join(appDir, "ABOUT.md"))) {
422
+ score += 2;
425
423
  }
426
- for (const entry of fs.readdirSync(cwd)) {
427
- const child = path.join(cwd, entry);
428
- const nestedPackages = path.join(child, "packages");
429
- if (!pathExists(nestedPackages)) {
430
- continue;
431
- }
432
- for (const nestedEntry of fs.readdirSync(nestedPackages)) {
433
- packageRoots.push(path.join(nestedPackages, nestedEntry));
434
- }
424
+ if (pathExists(path.join(appDir, "skills"))) {
425
+ score += 1;
435
426
  }
436
- for (const root of packageRoots) {
437
- const appDir = path.join(root, "app");
427
+ if (pathExists(path.join(appDir, "plugins"))) {
428
+ score += 1;
429
+ }
430
+ return score;
431
+ }
432
+ function resolveCandidateAppDirs(cwd, projectRoots) {
433
+ const roots = projectRoots ?? discoverProjectRoots(cwd);
434
+ const resolved = [];
435
+ const seen = /* @__PURE__ */ new Set();
436
+ for (const root of roots) {
437
+ const appDir = path.resolve(root, "app");
438
438
  if (!pathExists(appDir)) {
439
439
  continue;
440
440
  }
441
- candidates.push(appDir);
441
+ if (seen.has(appDir)) {
442
+ continue;
443
+ }
444
+ seen.add(appDir);
445
+ resolved.push(appDir);
442
446
  }
447
+ return resolved;
448
+ }
449
+ function resolveHomeDir(cwd = process.cwd(), options) {
450
+ const resolvedCwd = path.resolve(cwd);
451
+ const directApp = path.resolve(resolvedCwd, "app");
452
+ if (pathExists(directApp) && hasAnyDataMarkers(directApp)) {
453
+ return directApp;
454
+ }
455
+ const candidates = resolveCandidateAppDirs(resolvedCwd, options?.projectRoots);
443
456
  if (candidates.length === 0) {
444
457
  return directApp;
445
458
  }
446
459
  candidates.sort((left, right) => {
447
- const leftScore = Number(hasAnyDataMarkers(left));
448
- const rightScore = Number(hasAnyDataMarkers(right));
460
+ const leftScore = scoreAppCandidate(left);
461
+ const rightScore = scoreAppCandidate(right);
449
462
  if (leftScore !== rightScore) {
450
463
  return rightScore - leftScore;
451
464
  }
465
+ const leftDistance = path.relative(resolvedCwd, left).split(path.sep).length;
466
+ const rightDistance = path.relative(resolvedCwd, right).split(path.sep).length;
467
+ if (leftDistance !== rightDistance) {
468
+ return leftDistance - rightDistance;
469
+ }
452
470
  return left.localeCompare(right);
453
471
  });
454
472
  return candidates[0];
@@ -553,8 +571,8 @@ function createAppJwt(appId, privateKeyEnv) {
553
571
  const signature = signer.sign(getPrivateKey(privateKeyEnv)).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
554
572
  return `${signingInput}.${signature}`;
555
573
  }
556
- async function githubRequest(apiBase, path4, params) {
557
- const response = await fetch(`${apiBase}${path4}`, {
574
+ async function githubRequest(apiBase, path3, params) {
575
+ const response = await fetch(`${apiBase}${path3}`, {
558
576
  method: params.method ?? "GET",
559
577
  headers: {
560
578
  Accept: "application/vnd.github+json",
@@ -801,98 +819,6 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
801
819
  };
802
820
  }
803
821
 
804
- // src/chat/plugins/package-discovery.ts
805
- import { readFileSync, statSync } from "fs";
806
- import path2 from "path";
807
- function isDirectory(targetPath) {
808
- try {
809
- return statSync(targetPath).isDirectory();
810
- } catch {
811
- return false;
812
- }
813
- }
814
- function isFile(targetPath) {
815
- try {
816
- return statSync(targetPath).isFile();
817
- } catch {
818
- return false;
819
- }
820
- }
821
- function readRootPackageJson(cwd) {
822
- const rootPackageJsonPath = path2.join(cwd, "package.json");
823
- try {
824
- const raw = readFileSync(rootPackageJsonPath, "utf8");
825
- return JSON.parse(raw);
826
- } catch {
827
- return null;
828
- }
829
- }
830
- function uniqueSorted(values) {
831
- return [...new Set(values)].sort((left, right) => left.localeCompare(right));
832
- }
833
- function readInstalledDependencyNames(cwd) {
834
- const rootPackageJson = readRootPackageJson(cwd);
835
- if (!rootPackageJson) {
836
- return [];
837
- }
838
- return uniqueSorted([
839
- ...Object.keys(rootPackageJson.dependencies ?? {}),
840
- ...Object.keys(rootPackageJson.optionalDependencies ?? {})
841
- ]);
842
- }
843
- function packageInstallDir(cwd, packageName) {
844
- return path2.join(cwd, "node_modules", ...packageName.split("/"));
845
- }
846
- function discoverInstalledJuniorContentPackages(cwd = process.cwd()) {
847
- const dependencies = readInstalledDependencyNames(cwd);
848
- const discovered = [];
849
- for (const dependency of dependencies) {
850
- const dir = packageInstallDir(cwd, dependency);
851
- if (!isDirectory(dir)) {
852
- continue;
853
- }
854
- const hasRootPluginManifest = isFile(path2.join(dir, "plugin.yaml"));
855
- const hasPluginsDir = isDirectory(path2.join(dir, "plugins"));
856
- const hasSkillsDir = isDirectory(path2.join(dir, "skills"));
857
- if (!hasRootPluginManifest && !hasPluginsDir && !hasSkillsDir) {
858
- continue;
859
- }
860
- discovered.push({
861
- name: dependency,
862
- dir,
863
- hasRootPluginManifest,
864
- hasPluginsDir,
865
- hasSkillsDir
866
- });
867
- }
868
- return discovered;
869
- }
870
- function discoverInstalledPluginPackageContent(cwd = process.cwd()) {
871
- const manifestRoots = [];
872
- const skillRoots2 = [];
873
- const tracingIncludes = [];
874
- for (const pkg of discoverInstalledJuniorContentPackages(cwd)) {
875
- const base = `./node_modules/${pkg.name}`;
876
- if (pkg.hasRootPluginManifest) {
877
- manifestRoots.push(pkg.dir);
878
- tracingIncludes.push(`${base}/plugin.yaml`);
879
- }
880
- if (pkg.hasPluginsDir) {
881
- manifestRoots.push(path2.join(pkg.dir, "plugins"));
882
- tracingIncludes.push(`${base}/plugins/**/*`);
883
- }
884
- if (pkg.hasSkillsDir) {
885
- skillRoots2.push(path2.join(pkg.dir, "skills"));
886
- tracingIncludes.push(`${base}/skills/**/*`);
887
- }
888
- }
889
- return {
890
- manifestRoots: uniqueSorted(manifestRoots),
891
- skillRoots: uniqueSorted(skillRoots2),
892
- tracingIncludes: uniqueSorted(tracingIncludes)
893
- };
894
- }
895
-
896
822
  // src/chat/plugins/registry.ts
897
823
  var PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
898
824
  var SHORT_CAPABILITY_RE = /^[a-z0-9]+(\.[a-z0-9-]+)*$/;
@@ -1210,7 +1136,7 @@ function registerPluginManifest(raw, pluginDir) {
1210
1136
  const definition = {
1211
1137
  manifest,
1212
1138
  dir: pluginDir,
1213
- skillsDir: path3.join(pluginDir, "skills")
1139
+ skillsDir: path2.join(pluginDir, "skills")
1214
1140
  };
1215
1141
  pluginDefinitions.push(definition);
1216
1142
  pluginsByName.set(manifest.name, definition);
@@ -1231,7 +1157,7 @@ function loadPlugins() {
1231
1157
  let entries;
1232
1158
  let rootStat;
1233
1159
  try {
1234
- rootStat = statSync2(pluginsRoot);
1160
+ rootStat = statSync(pluginsRoot);
1235
1161
  } catch (error) {
1236
1162
  logWarn("plugin_root_read_failed", {}, {
1237
1163
  "file.directory": pluginsRoot,
@@ -1240,15 +1166,15 @@ function loadPlugins() {
1240
1166
  continue;
1241
1167
  }
1242
1168
  if (rootStat.isDirectory()) {
1243
- const manifestPath = path3.join(pluginsRoot, "plugin.yaml");
1169
+ const manifestPath = path2.join(pluginsRoot, "plugin.yaml");
1244
1170
  let hasRootManifest = false;
1245
1171
  try {
1246
- hasRootManifest = statSync2(manifestPath).isFile();
1172
+ hasRootManifest = statSync(manifestPath).isFile();
1247
1173
  } catch {
1248
1174
  hasRootManifest = false;
1249
1175
  }
1250
1176
  if (hasRootManifest) {
1251
- const rawRootManifest = readFileSync2(manifestPath, "utf8");
1177
+ const rawRootManifest = readFileSync(manifestPath, "utf8");
1252
1178
  registerPluginManifest(rawRootManifest, pluginsRoot);
1253
1179
  continue;
1254
1180
  }
@@ -1263,17 +1189,17 @@ function loadPlugins() {
1263
1189
  continue;
1264
1190
  }
1265
1191
  for (const entry of entries.sort()) {
1266
- const pluginDir = path3.join(pluginsRoot, entry);
1192
+ const pluginDir = path2.join(pluginsRoot, entry);
1267
1193
  try {
1268
- const stat = statSync2(pluginDir);
1194
+ const stat = statSync(pluginDir);
1269
1195
  if (!stat.isDirectory()) continue;
1270
1196
  } catch {
1271
1197
  continue;
1272
1198
  }
1273
- const manifestPath = path3.join(pluginDir, "plugin.yaml");
1199
+ const manifestPath = path2.join(pluginDir, "plugin.yaml");
1274
1200
  let raw;
1275
1201
  try {
1276
- raw = readFileSync2(manifestPath, "utf8");
1202
+ raw = readFileSync(manifestPath, "utf8");
1277
1203
  } catch {
1278
1204
  continue;
1279
1205
  }
@@ -25,7 +25,7 @@ import {
25
25
  skillRoots,
26
26
  soulPathCandidates,
27
27
  upsertAgentTurnSessionCheckpoint
28
- } from "./chunk-QHDDCUTN.js";
28
+ } from "./chunk-DPTR2FNH.js";
29
29
  import {
30
30
  logError,
31
31
  logException,
@@ -12,7 +12,7 @@ import {
12
12
  import { after } from "next/server";
13
13
  import * as Sentry from "@sentry/nextjs";
14
14
  async function loadBot() {
15
- const { bot } = await import("./bot-T73QBC4J.js");
15
+ const { bot } = await import("./bot-JSIREVQD.js");
16
16
  return bot;
17
17
  }
18
18
  async function POST(request, context) {
@@ -0,0 +1,328 @@
1
+ // src/chat/fs-utils.ts
2
+ import { statSync } from "fs";
3
+ function isDirectory(targetPath) {
4
+ try {
5
+ return statSync(targetPath).isDirectory();
6
+ } catch {
7
+ return false;
8
+ }
9
+ }
10
+ function isFile(targetPath) {
11
+ try {
12
+ return statSync(targetPath).isFile();
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ // src/chat/discovery-roots.ts
19
+ import { readdirSync } from "fs";
20
+ import path from "path";
21
+ import { fileURLToPath } from "url";
22
+ function normalizePath(targetPath) {
23
+ return path.resolve(targetPath);
24
+ }
25
+ function uniqueResolvedPathsInOrder(values) {
26
+ const seen = /* @__PURE__ */ new Set();
27
+ const resolved = [];
28
+ for (const value of values) {
29
+ const normalized = normalizePath(value);
30
+ if (seen.has(normalized)) {
31
+ continue;
32
+ }
33
+ seen.add(normalized);
34
+ resolved.push(normalized);
35
+ }
36
+ return resolved;
37
+ }
38
+ function isNodeModulesPath(candidatePath) {
39
+ return path.basename(candidatePath) === "node_modules";
40
+ }
41
+ function isInsidePnpmStore(candidatePath) {
42
+ return candidatePath.split(path.sep).includes(".pnpm");
43
+ }
44
+ function runningFromInstalledPackage() {
45
+ const currentFile = fileURLToPath(import.meta.url);
46
+ const marker = `${path.sep}node_modules${path.sep}@sentry${path.sep}junior${path.sep}`;
47
+ return currentFile.includes(marker);
48
+ }
49
+ function listInstalledPackageNodeModulesDirs() {
50
+ if (!runningFromInstalledPackage()) {
51
+ return [];
52
+ }
53
+ const dirs = [];
54
+ let current = path.resolve(path.dirname(fileURLToPath(import.meta.url)));
55
+ while (true) {
56
+ if (isNodeModulesPath(current) && !isInsidePnpmStore(current) && isDirectory(current)) {
57
+ dirs.push(current);
58
+ }
59
+ const parent = path.dirname(current);
60
+ if (parent === current) {
61
+ break;
62
+ }
63
+ current = parent;
64
+ }
65
+ return dirs;
66
+ }
67
+ function listCwdAncestorNodeModulesDirs(cwd) {
68
+ const resolvedCwd = normalizePath(cwd);
69
+ const dirs = [];
70
+ let current = resolvedCwd;
71
+ while (true) {
72
+ const nodeModulesDir = path.join(current, "node_modules");
73
+ if (isDirectory(nodeModulesDir)) {
74
+ dirs.push(nodeModulesDir);
75
+ }
76
+ if (isFile(path.join(current, "package.json"))) {
77
+ break;
78
+ }
79
+ const parent = path.dirname(current);
80
+ if (parent === current) {
81
+ break;
82
+ }
83
+ current = parent;
84
+ }
85
+ return dirs;
86
+ }
87
+ function discoverNodeModulesDirs(cwd = process.cwd(), options) {
88
+ const explicit = options?.candidateDirs?.filter((dir) => isDirectory(dir)) ?? [];
89
+ if (explicit.length > 0) {
90
+ return uniqueResolvedPathsInOrder(explicit);
91
+ }
92
+ return uniqueResolvedPathsInOrder([
93
+ ...listInstalledPackageNodeModulesDirs(),
94
+ ...listCwdAncestorNodeModulesDirs(cwd)
95
+ ]);
96
+ }
97
+ function discoverProjectRoots(cwd = process.cwd(), options) {
98
+ const roots = discoverNodeModulesDirs(cwd, options?.nodeModulesDirs ? { candidateDirs: options.nodeModulesDirs } : void 0).map((nodeModulesDir) => path.dirname(nodeModulesDir));
99
+ return uniqueResolvedPathsInOrder([cwd, ...roots]);
100
+ }
101
+ function listTopLevelPackages(nodeModulesDir) {
102
+ const entries = readdirSync(nodeModulesDir, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".") && entry.name !== ".bin" && entry.name !== ".pnpm").sort((left, right) => left.name.localeCompare(right.name));
103
+ const packages = [];
104
+ for (const entry of entries) {
105
+ const entryPath = path.join(nodeModulesDir, entry.name);
106
+ if (entry.name.startsWith("@")) {
107
+ if (!isDirectory(entryPath)) {
108
+ continue;
109
+ }
110
+ const scopedEntries = readdirSync(entryPath, { withFileTypes: true }).sort(
111
+ (left, right) => left.name.localeCompare(right.name)
112
+ );
113
+ for (const scopedEntry of scopedEntries) {
114
+ const packageName = `${entry.name}/${scopedEntry.name}`;
115
+ const packagePath = path.join(entryPath, scopedEntry.name);
116
+ if (!isDirectory(packagePath)) {
117
+ continue;
118
+ }
119
+ packages.push({ name: packageName, dir: packagePath });
120
+ }
121
+ continue;
122
+ }
123
+ if (!isDirectory(entryPath)) {
124
+ continue;
125
+ }
126
+ packages.push({ name: entry.name, dir: entryPath });
127
+ }
128
+ return packages;
129
+ }
130
+
131
+ // src/chat/plugins/package-discovery.ts
132
+ import { createRequire } from "module";
133
+ import path2 from "path";
134
+ var require2 = createRequire(import.meta.url);
135
+ function normalizeForGlob(targetPath) {
136
+ return targetPath.split(path2.sep).join("/");
137
+ }
138
+ function uniqueStringsInOrder(values) {
139
+ const seen = /* @__PURE__ */ new Set();
140
+ const resolved = [];
141
+ for (const value of values) {
142
+ if (seen.has(value)) {
143
+ continue;
144
+ }
145
+ seen.add(value);
146
+ resolved.push(value);
147
+ }
148
+ return resolved;
149
+ }
150
+ function pathWithinCwd(cwd, targetPath) {
151
+ const relative = path2.relative(cwd, targetPath);
152
+ if (!relative || relative.startsWith("..") || path2.isAbsolute(relative)) {
153
+ return null;
154
+ }
155
+ return `./${normalizeForGlob(relative)}`;
156
+ }
157
+ function parseRuntimeConfiguredPackageNames(value) {
158
+ if (!Array.isArray(value)) {
159
+ return null;
160
+ }
161
+ const parsed = value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
162
+ return uniqueStringsInOrder(parsed.map((entry) => entry.trim()));
163
+ }
164
+ function readNextRuntimeConfiguredPackageNames() {
165
+ try {
166
+ const nextConfigModule = require2("next/config");
167
+ const getConfig = nextConfigModule.default;
168
+ if (typeof getConfig !== "function") {
169
+ return null;
170
+ }
171
+ const runtimeConfig = getConfig();
172
+ return parseRuntimeConfiguredPackageNames(runtimeConfig?.serverRuntimeConfig?.juniorPluginPackages) ?? [];
173
+ } catch {
174
+ return null;
175
+ }
176
+ }
177
+ function findContainingNodeModulesDir(targetPath) {
178
+ let current = path2.resolve(targetPath);
179
+ while (true) {
180
+ if (path2.basename(current) === "node_modules") {
181
+ return current;
182
+ }
183
+ const parent = path2.dirname(current);
184
+ if (parent === current) {
185
+ return null;
186
+ }
187
+ current = parent;
188
+ }
189
+ }
190
+ function resolvePackageDirFromName(packageName, candidateNodeModulesDirs) {
191
+ for (const nodeModulesDir of candidateNodeModulesDirs) {
192
+ const packageDir = path2.join(nodeModulesDir, ...packageName.split("/"));
193
+ if (isDirectory(packageDir)) {
194
+ return {
195
+ dir: path2.resolve(packageDir),
196
+ nodeModulesDir: path2.resolve(nodeModulesDir)
197
+ };
198
+ }
199
+ }
200
+ try {
201
+ const packageJsonPath = require2.resolve(`${packageName}/package.json`);
202
+ const dir = path2.dirname(packageJsonPath);
203
+ const nodeModulesDir = findContainingNodeModulesDir(dir);
204
+ if (!nodeModulesDir) {
205
+ return null;
206
+ }
207
+ return {
208
+ dir: path2.resolve(dir),
209
+ nodeModulesDir: path2.resolve(nodeModulesDir)
210
+ };
211
+ } catch {
212
+ return null;
213
+ }
214
+ }
215
+ function discoverDeclaredPackages(packageNames, candidateNodeModulesDirs) {
216
+ const discovered = [];
217
+ const seenPackageNames = /* @__PURE__ */ new Set();
218
+ const seenPackageDirs = /* @__PURE__ */ new Set();
219
+ for (const packageName of packageNames) {
220
+ const resolved = resolvePackageDirFromName(packageName, candidateNodeModulesDirs);
221
+ if (!resolved) {
222
+ continue;
223
+ }
224
+ if (seenPackageNames.has(packageName) || seenPackageDirs.has(resolved.dir)) {
225
+ continue;
226
+ }
227
+ const hasRootPluginManifest = isFile(path2.join(resolved.dir, "plugin.yaml"));
228
+ const hasPluginsDir = isDirectory(path2.join(resolved.dir, "plugins"));
229
+ const hasSkillsDir = isDirectory(path2.join(resolved.dir, "skills"));
230
+ if (!hasRootPluginManifest && !hasPluginsDir && !hasSkillsDir) {
231
+ continue;
232
+ }
233
+ seenPackageNames.add(packageName);
234
+ seenPackageDirs.add(resolved.dir);
235
+ discovered.push({
236
+ name: packageName,
237
+ dir: resolved.dir,
238
+ nodeModulesDir: resolved.nodeModulesDir,
239
+ hasRootPluginManifest,
240
+ hasPluginsDir,
241
+ hasSkillsDir
242
+ });
243
+ }
244
+ return discovered;
245
+ }
246
+ function discoverInstalledJuniorContentPackages(cwd = process.cwd(), nodeModulesDirs, packageNames) {
247
+ const resolvedCwd = path2.resolve(cwd);
248
+ const candidateNodeModulesDirs = nodeModulesDirs ?? discoverNodeModulesDirs(resolvedCwd);
249
+ const configuredPackageNames = packageNames ?? readNextRuntimeConfiguredPackageNames();
250
+ const declaredPackages = discoverDeclaredPackages(configuredPackageNames ?? [], candidateNodeModulesDirs);
251
+ const useFallbackScan = configuredPackageNames === null;
252
+ const discovered = [...declaredPackages];
253
+ const seenPackageNames = /* @__PURE__ */ new Set();
254
+ const seenPackageDirs = /* @__PURE__ */ new Set();
255
+ for (const pkg of declaredPackages) {
256
+ seenPackageNames.add(pkg.name);
257
+ seenPackageDirs.add(pkg.dir);
258
+ }
259
+ if (!useFallbackScan) {
260
+ return discovered;
261
+ }
262
+ for (const nodeModulesDir of candidateNodeModulesDirs) {
263
+ for (const pkg of listTopLevelPackages(nodeModulesDir)) {
264
+ const resolvedDir = path2.resolve(pkg.dir);
265
+ if (seenPackageNames.has(pkg.name) || seenPackageDirs.has(resolvedDir)) {
266
+ continue;
267
+ }
268
+ seenPackageNames.add(pkg.name);
269
+ seenPackageDirs.add(resolvedDir);
270
+ const hasRootPluginManifest = isFile(path2.join(resolvedDir, "plugin.yaml"));
271
+ const hasPluginsDir = isDirectory(path2.join(resolvedDir, "plugins"));
272
+ const hasSkillsDir = isDirectory(path2.join(resolvedDir, "skills"));
273
+ if (!hasRootPluginManifest && !hasPluginsDir && !hasSkillsDir) {
274
+ continue;
275
+ }
276
+ discovered.push({
277
+ name: pkg.name,
278
+ dir: resolvedDir,
279
+ nodeModulesDir: path2.resolve(nodeModulesDir),
280
+ hasRootPluginManifest,
281
+ hasPluginsDir,
282
+ hasSkillsDir
283
+ });
284
+ }
285
+ }
286
+ return discovered;
287
+ }
288
+ function discoverInstalledPluginPackageContent(cwd = process.cwd(), options) {
289
+ const resolvedCwd = path2.resolve(cwd);
290
+ const discoveredPackages = discoverInstalledJuniorContentPackages(resolvedCwd, options?.nodeModulesDirs, options?.packageNames);
291
+ const manifestRoots = [];
292
+ const skillRoots = [];
293
+ const tracingIncludes = [];
294
+ for (const pkg of discoveredPackages) {
295
+ const packagePathFromNodeModules = pathWithinCwd(resolvedCwd, path2.join(pkg.nodeModulesDir, ...pkg.name.split("/")));
296
+ if (pkg.hasRootPluginManifest) {
297
+ manifestRoots.push(pkg.dir);
298
+ if (packagePathFromNodeModules) {
299
+ tracingIncludes.push(`${packagePathFromNodeModules}/plugin.yaml`);
300
+ }
301
+ }
302
+ if (pkg.hasPluginsDir) {
303
+ manifestRoots.push(path2.join(pkg.dir, "plugins"));
304
+ if (packagePathFromNodeModules) {
305
+ tracingIncludes.push(`${packagePathFromNodeModules}/plugins/**/*`);
306
+ }
307
+ }
308
+ if (pkg.hasSkillsDir) {
309
+ skillRoots.push(path2.join(pkg.dir, "skills"));
310
+ if (packagePathFromNodeModules) {
311
+ tracingIncludes.push(`${packagePathFromNodeModules}/skills/**/*`);
312
+ }
313
+ }
314
+ }
315
+ return {
316
+ packageNames: uniqueStringsInOrder(discoveredPackages.map((pkg) => pkg.name)),
317
+ manifestRoots: uniqueStringsInOrder(manifestRoots),
318
+ skillRoots: uniqueStringsInOrder(skillRoots),
319
+ tracingIncludes: uniqueStringsInOrder(tracingIncludes)
320
+ };
321
+ }
322
+
323
+ export {
324
+ isDirectory,
325
+ discoverNodeModulesDirs,
326
+ discoverProjectRoots,
327
+ discoverInstalledPluginPackageContent
328
+ };
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  disconnectStateAdapter,
3
3
  resolveRuntimeDependencySnapshot
4
- } from "../chunk-QHDDCUTN.js";
4
+ } from "../chunk-DPTR2FNH.js";
5
5
  import "../chunk-PY4AI2GZ.js";
6
+ import "../chunk-SP6LV35L.js";
6
7
 
7
8
  // src/cli/snapshot-warmup.ts
8
9
  var DEFAULT_RUNTIME = "node22";
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  POST
3
- } from "../chunk-TQSDLJVE.js";
4
- import "../chunk-RXNMJQPY.js";
5
- import "../chunk-QHDDCUTN.js";
3
+ } from "../chunk-4RFOJSJL.js";
4
+ import "../chunk-L745IWNK.js";
5
+ import "../chunk-DPTR2FNH.js";
6
6
  import "../chunk-PY4AI2GZ.js";
7
+ import "../chunk-SP6LV35L.js";
7
8
  export {
8
9
  POST
9
10
  };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  POST as POST2
3
- } from "../chunk-JDBWDYGR.js";
3
+ } from "../chunk-RTQMRGZD.js";
4
4
  import {
5
5
  POST
6
- } from "../chunk-TQSDLJVE.js";
6
+ } from "../chunk-4RFOJSJL.js";
7
7
  import {
8
8
  escapeXml,
9
9
  generateAssistantReply,
@@ -13,18 +13,19 @@ import {
13
13
  publishAppHomeView,
14
14
  resolveBaseUrl,
15
15
  truncateStatusText
16
- } from "../chunk-RXNMJQPY.js";
16
+ } from "../chunk-L745IWNK.js";
17
17
  import {
18
18
  GET
19
19
  } from "../chunk-4RBEYCOG.js";
20
20
  import {
21
21
  botConfig,
22
22
  getStateAdapter
23
- } from "../chunk-QHDDCUTN.js";
23
+ } from "../chunk-DPTR2FNH.js";
24
24
  import {
25
25
  logException,
26
26
  logInfo
27
27
  } from "../chunk-PY4AI2GZ.js";
28
+ import "../chunk-SP6LV35L.js";
28
29
 
29
30
  // src/handlers/oauth-callback.ts
30
31
  import { after } from "next/server";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  POST
3
- } from "../chunk-JDBWDYGR.js";
3
+ } from "../chunk-RTQMRGZD.js";
4
4
  import "../chunk-PY4AI2GZ.js";
5
5
  export {
6
6
  POST
@@ -7,7 +7,7 @@ interface JuniorConfigOptions {
7
7
  dataDir?: string;
8
8
  skillsDir?: string;
9
9
  pluginsDir?: string;
10
- sentry?: boolean;
10
+ pluginPackages?: string[];
11
11
  }
12
12
  type NextConfigFactory = (phase: string, ctx: {
13
13
  defaultConfig: NextConfig;
@@ -17,6 +17,6 @@ type NextConfigFactory = (phase: string, ctx: {
17
17
  *
18
18
  * Supports both object and function-style Next config exports.
19
19
  */
20
- declare function withJunior(nextConfig?: NextConfig | NextConfigFactory, options?: JuniorConfigOptions): NextConfig | NextConfigFactory;
20
+ declare function withJunior(options?: JuniorConfigOptions, nextConfig?: NextConfig | NextConfigFactory): NextConfig | NextConfigFactory;
21
21
 
22
22
  export { type JuniorConfigOptions, withJunior };
@@ -1,59 +1,53 @@
1
+ import {
2
+ discoverInstalledPluginPackageContent,
3
+ discoverNodeModulesDirs,
4
+ isDirectory
5
+ } from "./chunk-SP6LV35L.js";
6
+
1
7
  // src/next-config.ts
2
8
  import { createRequire } from "module";
3
- import { readFileSync, statSync } from "fs";
4
9
  import path from "path";
5
10
  var require2 = createRequire(import.meta.url);
6
- function isDirectory(targetPath) {
7
- try {
8
- return statSync(targetPath).isDirectory();
9
- } catch {
10
- return false;
11
- }
11
+ function unique(values) {
12
+ return [...new Set(values)];
12
13
  }
13
- function isFile(targetPath) {
14
- try {
15
- return statSync(targetPath).isFile();
16
- } catch {
17
- return false;
18
- }
14
+ function sentryConfigured() {
15
+ return Boolean(process.env.NEXT_PUBLIC_SENTRY_DSN || process.env.SENTRY_DSN);
19
16
  }
20
- function discoverInstalledPluginPackageTracingIncludes(cwd = process.cwd()) {
21
- const rootPackageJsonPath = path.join(cwd, "package.json");
22
- let rootPackageJson;
23
- try {
24
- rootPackageJson = JSON.parse(readFileSync(rootPackageJsonPath, "utf8"));
25
- } catch {
26
- return [];
27
- }
28
- const dependencies = [
29
- ...Object.keys(rootPackageJson.dependencies ?? {}),
30
- ...Object.keys(rootPackageJson.optionalDependencies ?? {})
31
- ];
32
- const tracingIncludes = [];
33
- for (const dependency of dependencies) {
34
- const packageDir = path.join(cwd, "node_modules", ...dependency.split("/"));
35
- if (!isDirectory(packageDir)) {
36
- continue;
37
- }
38
- const base = `./node_modules/${dependency}`;
39
- if (isFile(path.join(packageDir, "plugin.yaml"))) {
40
- tracingIncludes.push(`${base}/plugin.yaml`);
41
- }
42
- if (isDirectory(path.join(packageDir, "plugins"))) {
43
- tracingIncludes.push(`${base}/plugins/**/*`);
44
- }
45
- if (isDirectory(path.join(packageDir, "skills"))) {
46
- tracingIncludes.push(`${base}/skills/**/*`);
47
- }
48
- }
49
- return [...new Set(tracingIncludes)].sort((left, right) => left.localeCompare(right));
17
+ function isPackageInstalled(cwd, packageName) {
18
+ const nodeModulesDirs = discoverNodeModulesDirs(cwd);
19
+ return nodeModulesDirs.some(
20
+ (nodeModulesDir) => isDirectory(path.join(nodeModulesDir, ...packageName.split("/")))
21
+ );
50
22
  }
51
23
  function applyJuniorConfig(nextConfig, options) {
24
+ const existingServerRuntimeConfig = nextConfig?.serverRuntimeConfig ?? {};
52
25
  const dataDir = options?.dataDir ?? "./app/data";
53
26
  const skillsDir = options?.skillsDir ?? "./app/skills";
54
27
  const pluginsDir = options?.pluginsDir ?? "./app/plugins";
28
+ const configuredPluginPackages = unique(options?.pluginPackages ?? []);
29
+ const discoveredPlugins = discoverInstalledPluginPackageContent(process.cwd(), { packageNames: configuredPluginPackages });
30
+ const unresolvedConfiguredPackages = configuredPluginPackages.filter(
31
+ (packageName) => !discoveredPlugins.packageNames.includes(packageName)
32
+ );
33
+ const invalidPluginPackages = unresolvedConfiguredPackages.filter(
34
+ (packageName) => isPackageInstalled(process.cwd(), packageName)
35
+ );
36
+ const missingPluginPackages = unresolvedConfiguredPackages.filter(
37
+ (packageName) => !invalidPluginPackages.includes(packageName)
38
+ );
39
+ if (invalidPluginPackages.length > 0) {
40
+ throw new Error(
41
+ `withJunior pluginPackages contains installed packages that are not valid Junior plugins: ${invalidPluginPackages.join(", ")}`
42
+ );
43
+ }
44
+ if (missingPluginPackages.length > 0) {
45
+ throw new Error(
46
+ `withJunior pluginPackages contains unresolved packages: ${missingPluginPackages.join(", ")}`
47
+ );
48
+ }
55
49
  const defaultDataTracingIncludes = options?.dataDir ? [`${dataDir}/**/*`] : ["./app/SOUL.md", "./app/ABOUT.md"];
56
- const pluginPackageTracingIncludes = discoverInstalledPluginPackageTracingIncludes();
50
+ const pluginPackageTracingIncludes = discoveredPlugins.tracingIncludes;
57
51
  const tracingIncludes = Array.from(/* @__PURE__ */ new Set([
58
52
  ...defaultDataTracingIncludes,
59
53
  `${skillsDir}/**/*`,
@@ -69,6 +63,7 @@ function applyJuniorConfig(nextConfig, options) {
69
63
  ...nextConfig,
70
64
  serverExternalPackages: Array.from(/* @__PURE__ */ new Set([
71
65
  ...nextConfig?.serverExternalPackages ?? [],
66
+ "@vercel/queue",
72
67
  "@vercel/sandbox",
73
68
  "bash-tool",
74
69
  "just-bash",
@@ -80,23 +75,27 @@ function applyJuniorConfig(nextConfig, options) {
80
75
  outputFileTracingIncludes: {
81
76
  ...nextConfig?.outputFileTracingIncludes,
82
77
  "/*": mergedGlobalTracingIncludes
78
+ },
79
+ serverRuntimeConfig: {
80
+ ...existingServerRuntimeConfig,
81
+ juniorPluginPackages: configuredPluginPackages
83
82
  }
84
83
  };
85
- if (options?.sentry) {
86
- const { withSentryConfig } = require2("@sentry/nextjs");
87
- return withSentryConfig(config, {
88
- org: process.env.SENTRY_ORG,
89
- project: process.env.SENTRY_PROJECT,
90
- authToken: process.env.SENTRY_AUTH_TOKEN,
91
- silent: !process.env.CI,
92
- sourcemaps: {
93
- disable: false
94
- }
95
- });
84
+ if (!sentryConfigured()) {
85
+ return config;
96
86
  }
97
- return config;
87
+ const { withSentryConfig } = require2("@sentry/nextjs");
88
+ return withSentryConfig(config, {
89
+ org: process.env.SENTRY_ORG,
90
+ project: process.env.SENTRY_PROJECT,
91
+ authToken: process.env.SENTRY_AUTH_TOKEN,
92
+ silent: !process.env.CI,
93
+ sourcemaps: {
94
+ disable: false
95
+ }
96
+ });
98
97
  }
99
- function withJunior(nextConfig, options) {
98
+ function withJunior(options, nextConfig) {
100
99
  if (typeof nextConfig === "function") {
101
100
  return async (phase, ctx) => {
102
101
  const resolved = await nextConfig(phase, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"