specrails-desktop 2.59.2 → 2.59.3

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 (87) hide show
  1. package/client/dist/assets/{ActivityFeedPage-vAY9C71W.js → ActivityFeedPage-BGEayjTY.js} +1 -1
  2. package/client/dist/assets/{AgentBrowserCapture-CpwjGXQm.js → AgentBrowserCapture-Qvg4y5Tr.js} +1 -1
  3. package/client/dist/assets/{AgentModeAnalyticsPane-BHeoJVxQ.js → AgentModeAnalyticsPane-CR7aFeLJ.js} +2 -2
  4. package/client/dist/assets/{AgentModeCodePane-B0Nf3YkN.js → AgentModeCodePane-gAd88Gng.js} +2 -2
  5. package/client/dist/assets/{AgentModeJobsPane-CpbIItY_.js → AgentModeJobsPane-EjCDuTow.js} +1 -1
  6. package/client/dist/assets/{AgentsPage-BVJAFXn3.js → AgentsPage-CRHHcp1V.js} +1 -1
  7. package/client/dist/assets/{AnalyticsPage-DDWees-K.js → AnalyticsPage-a0b1fNEl.js} +1 -1
  8. package/client/dist/assets/{CodePage-CzV5tkvS.js → CodePage-BmV1m7l3.js} +1 -1
  9. package/client/dist/assets/{DesktopAnalyticsPage-DfRHNK7i.js → DesktopAnalyticsPage-B87WmXOQ.js} +1 -1
  10. package/client/dist/assets/{DocsDialog-BxduBYbh.js → DocsDialog-Co0O672q.js} +1 -1
  11. package/client/dist/assets/{DocsPage-DOVeRJn_.js → DocsPage-DxGI6A2T.js} +1 -1
  12. package/client/dist/assets/{ExportDropdown-BnKVV8UK.js → ExportDropdown-1Ce031vB.js} +1 -1
  13. package/client/dist/assets/{InteractiveJobComposer-Da9dvM-0.js → InteractiveJobComposer-DkqR77pO.js} +2 -2
  14. package/client/dist/assets/JobDetailModal-DVLDVgzz.js +1 -0
  15. package/client/dist/assets/JobDetailPage-RcP9N-D-.js +1 -0
  16. package/client/dist/assets/{JobsPage-0vdlbNik.js → JobsPage-R9M4xQnp.js} +1 -1
  17. package/client/dist/assets/{LoopBuilderPage-DCUHozXh.js → LoopBuilderPage-C4S-rkWp.js} +1 -1
  18. package/client/dist/assets/{LoopPreviewModal-Df-S8NGf.js → LoopPreviewModal-DA2JeKWX.js} +1 -1
  19. package/client/dist/assets/{LoopWindowSurface-sdb7QbSo.js → LoopWindowSurface-BkkeD0hx.js} +1 -1
  20. package/client/dist/assets/{LoopsPage-CDfCC9Px.js → LoopsPage-BHv--iI7.js} +1 -1
  21. package/client/dist/assets/{MinimizedChatsContext-DKlPf54Y.js → MinimizedChatsContext-Bc9xHByW.js} +1 -1
  22. package/client/dist/assets/PluginsPage-BEy62gxK.js +2 -0
  23. package/client/dist/assets/{ProjectSettingsDialog-DOkjT7xv.js → ProjectSettingsDialog-DDQ_f94T.js} +1 -1
  24. package/client/dist/assets/{RepositoryDeliveries-CJVpOUpZ.js → RepositoryDeliveries-DIWDFLRM.js} +1 -1
  25. package/client/dist/assets/{RepositoryScopeSelector-BeM-2gIZ.js → RepositoryScopeSelector-D3PlHIM-.js} +1 -1
  26. package/client/dist/assets/{ReviewPacketPage-EBCim2w_.js → ReviewPacketPage-BXdPwmdE.js} +1 -1
  27. package/client/dist/assets/{TemplatePreviewModal-1-92UGwN.js → TemplatePreviewModal-C7-G6lvI.js} +1 -1
  28. package/client/dist/assets/{TicketDetailModalContext-CkVXuHxU.js → TicketDetailModalContext-gGUv7gsQ.js} +1 -1
  29. package/client/dist/assets/{Trans-CzhPjnjB.js → Trans-Br_OIUKP.js} +1 -1
  30. package/client/dist/assets/{formatDistanceToNow-DlQaXc7c.js → formatDistanceToNow-Q7QPRFV4.js} +1 -1
  31. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-DxUFGcVq.js → getTimezoneOffsetInMilliseconds-Bq0CXBaJ.js} +1 -1
  32. package/client/dist/assets/{index-Czov72g7.js → index-DVkWHOR3.js} +7 -7
  33. package/client/dist/assets/jira-B6OYw4CJ.js +1 -0
  34. package/client/dist/assets/jira-BTF6RaOe.js +1 -0
  35. package/client/dist/assets/jira-CEetIB2q.js +1 -0
  36. package/client/dist/assets/jira-CIzsZLw5.js +1 -0
  37. package/client/dist/assets/{jira-BOoaUXbb.js → jira-Cq6yvN4q.js} +1 -1
  38. package/client/dist/assets/jira-Daw9tmpI.js +1 -0
  39. package/client/dist/assets/jira-FXr7u2lh.js +1 -0
  40. package/client/dist/assets/jira-LaNQn7yl.js +1 -0
  41. package/client/dist/assets/{jira-api-CbJvbRYx.js → jira-api-C2Tzc362.js} +1 -1
  42. package/client/dist/assets/{jobs-DZAbh1m2.js → jobs-ARwD85B9.js} +1 -1
  43. package/client/dist/assets/{jobs-BD47Ov11.js → jobs-BMF1w4WT.js} +1 -1
  44. package/client/dist/assets/{jobs-tWjcDF_6.js → jobs-CBfzGLwm.js} +1 -1
  45. package/client/dist/assets/{jobs-CSrRx5lD.js → jobs-CPjiluBY.js} +1 -1
  46. package/client/dist/assets/{jobs-epP7gt8W.js → jobs-CQQd58pY.js} +1 -1
  47. package/client/dist/assets/{jobs-1G3wodhl.js → jobs-De3EqaO0.js} +1 -1
  48. package/client/dist/assets/{jobs-BLOoVRRl.js → jobs-Du9-Ei9a.js} +1 -1
  49. package/client/dist/assets/{jobs-B4pu8TdF.js → jobs-qMHbnxwC.js} +1 -1
  50. package/client/dist/assets/{project-repositories-BkBD3rxx.js → project-repositories-DZKmieH3.js} +1 -1
  51. package/client/dist/assets/{spending-CYotH-9N.js → spending-EXaboRU0.js} +1 -1
  52. package/client/dist/assets/{useDesktop-DpxVGfUC.js → useDesktop-ChSmmJfc.js} +1 -1
  53. package/client/dist/assets/{useRuntimeRuns-CfGRbhr9.js → useRuntimeRuns-w9-5OzZl.js} +1 -1
  54. package/client/dist/assets/{useSharedWebSocket-C17TGqT8.js → useSharedWebSocket-g0nTHfTT.js} +2 -2
  55. package/client/dist/index.html +16 -16
  56. package/docs/internals/configuration.md +45 -0
  57. package/docs/internals/loop-step-log-explorer.md +27 -0
  58. package/docs/internals/mission-rail-cards.md +7 -0
  59. package/docs/internals/safe-pr-review-flow.md +9 -0
  60. package/docs/internals/source-map.md +5 -1
  61. package/docs/jira-integration-plan.md +7 -4
  62. package/package.json +1 -1
  63. package/server/dist/core-execution.js +5 -18
  64. package/server/dist/jira/jira-db.js +33 -6
  65. package/server/dist/jira/jira-materializer.js +11 -9
  66. package/server/dist/jira/jira-status-resolver.js +64 -22
  67. package/server/dist/jira/jira-sync-manager.js +40 -1
  68. package/server/dist/jira-router.js +8 -41
  69. package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +15 -67
  70. package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls.js +7 -4
  71. package/server/dist/modules/agent-runtime/runtime/agent-runtime-verification-log.js +252 -0
  72. package/server/dist/modules/delivery/runtime/rail-relaunch.js +10 -2
  73. package/server/dist/modules/loops/runtime/loop-run-manager.js +5 -3
  74. package/server/dist/path-resolver.js +41 -20
  75. package/server/dist/project-env.js +22 -6
  76. package/server/dist/util/checkout-path.js +22 -0
  77. package/server/dist/worktree-node-modules.js +70 -34
  78. package/client/dist/assets/JobDetailModal-DhwKbZGe.js +0 -1
  79. package/client/dist/assets/JobDetailPage-MtPE-E_1.js +0 -1
  80. package/client/dist/assets/PluginsPage-iL0G3rQQ.js +0 -2
  81. package/client/dist/assets/jira-BNZu8f9O.js +0 -1
  82. package/client/dist/assets/jira-BYVN4L2u.js +0 -1
  83. package/client/dist/assets/jira-CKJg3HZd.js +0 -1
  84. package/client/dist/assets/jira-DDDsZ4ub.js +0 -1
  85. package/client/dist/assets/jira-DXwYjVE4.js +0 -1
  86. package/client/dist/assets/jira-Dn6U2nRW.js +0 -1
  87. package/client/dist/assets/jira-j7hVolR5.js +0 -1
@@ -12,6 +12,7 @@ exports.parseLoginShellOutput = parseLoginShellOutput;
12
12
  exports.augmentPathFromLoginShell = augmentPathFromLoginShell;
13
13
  exports.parseLoginShellEnv = parseLoginShellEnv;
14
14
  exports.augmentEnvFromLoginShell = augmentEnvFromLoginShell;
15
+ exports.readEnvFromLoginShellSync = readEnvFromLoginShellSync;
15
16
  exports.augmentEnvFromLoginShellSync = augmentEnvFromLoginShellSync;
16
17
  exports.augmentAuthEnvFromLoginShell = augmentAuthEnvFromLoginShell;
17
18
  exports.getPathDiagnostic = getPathDiagnostic;
@@ -372,6 +373,17 @@ function parseLoginShellOutput(stdout) {
372
373
  return null;
373
374
  return stdout.slice(start, end);
374
375
  }
376
+ /** GUI launches may omit SHELL even when the account uses zsh/bash. */
377
+ function resolveLoginShell(env = process.env) {
378
+ if (env.SHELL)
379
+ return env.SHELL;
380
+ try {
381
+ return os_1.default.userInfo().shell || '/bin/sh';
382
+ }
383
+ catch {
384
+ return '/bin/sh';
385
+ }
386
+ }
375
387
  /**
376
388
  * Spawn the user's login shell once and merge any additional PATH segments
377
389
  * it exposes (Volta/nvm/fnm/asdf shims) into `process.env.PATH`. Async,
@@ -398,7 +410,7 @@ async function augmentPathFromLoginShell(opts = {}) {
398
410
  }
399
411
  const spawnFn = opts.spawnFn ?? child_process_1.spawn;
400
412
  const timeoutMs = opts.timeoutMs ?? LOGIN_SHELL_TIMEOUT_MS;
401
- const shell = process.env.SHELL || '/bin/sh';
413
+ const shell = resolveLoginShell();
402
414
  const command = `printf "${PATH_BEGIN}%s${PATH_END}" "$PATH"`;
403
415
  const status = await new Promise((resolve) => {
404
416
  let child;
@@ -474,7 +486,6 @@ exports.AUTH_ENV_VARS = [
474
486
  'GOOGLE_APPLICATION_CREDENTIALS',
475
487
  ];
476
488
  const LOGIN_SHELL_ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
477
- const loginShellEnvProbeCache = new Set();
478
489
  function normalizeLoginShellEnvNames(names) {
479
490
  const out = [];
480
491
  for (const raw of names) {
@@ -528,7 +539,7 @@ async function augmentEnvFromLoginShell(names, opts = {}) {
528
539
  return;
529
540
  const spawnFn = opts.spawnFn ?? child_process_1.spawn;
530
541
  const timeoutMs = opts.timeoutMs ?? LOGIN_SHELL_TIMEOUT_MS;
531
- const shell = process.env.SHELL || '/bin/sh';
542
+ const shell = resolveLoginShell();
532
543
  const command = buildLoginShellEnvCommand(wanted);
533
544
  await new Promise((resolve) => {
534
545
  let child;
@@ -569,42 +580,53 @@ async function augmentEnvFromLoginShell(names, opts = {}) {
569
580
  });
570
581
  });
571
582
  }
572
- /** Synchronous, cached variant for spawn-time env assembly. It probes each
573
- * missing name at most once per process to keep loops from paying the login
574
- * shell timeout on every step. */
575
- function augmentEnvFromLoginShellSync(names, opts = {}) {
583
+ /** Read missing configured names without mutating the caller's or global env.
584
+ * Callers own any cache so project credentials cannot leak across scopes. */
585
+ function readEnvFromLoginShellSync(names, opts = {}) {
576
586
  if (process.platform === 'win32')
577
- return;
587
+ return {};
578
588
  if (process.env.NODE_ENV === 'test' || process.env.VITEST === 'true')
579
- return;
580
- const wanted = normalizeLoginShellEnvNames(names).filter((name) => !process.env[name] && !loginShellEnvProbeCache.has(name));
589
+ return {};
590
+ const env = opts.env ?? process.env;
591
+ const wanted = normalizeLoginShellEnvNames(names).filter((name) => !env[name]);
581
592
  if (wanted.length === 0)
582
- return;
583
- for (const name of wanted)
584
- loginShellEnvProbeCache.add(name);
593
+ return {};
585
594
  const spawnSyncFn = opts.spawnSyncFn ?? child_process_1.spawnSync;
586
595
  const timeoutMs = opts.timeoutMs ?? LOGIN_SHELL_TIMEOUT_MS;
587
- const shell = process.env.SHELL || '/bin/sh';
596
+ const shell = resolveLoginShell(env);
588
597
  const command = buildLoginShellEnvCommand(wanted);
589
598
  let res;
590
599
  try {
591
600
  res = spawnSyncFn(shell, ['-l', '-i', '-c', command], {
601
+ env,
592
602
  encoding: 'utf8',
593
603
  stdio: ['ignore', 'pipe', 'pipe'],
594
604
  timeout: timeoutMs,
605
+ maxBuffer: 1024 * 1024,
595
606
  });
596
607
  }
597
608
  catch {
598
- return;
609
+ return {};
599
610
  }
600
- if (res.error)
601
- return;
611
+ if (res.error || res.status !== 0)
612
+ return {};
602
613
  const stdout = typeof res.stdout === 'string' ? res.stdout : res.stdout?.toString('utf8') ?? '';
603
614
  const recovered = parseLoginShellEnv(stdout);
615
+ const out = {};
604
616
  for (const key of wanted) {
605
617
  const val = recovered[key];
606
- if (val && !process.env[key])
607
- process.env[key] = val;
618
+ if (val)
619
+ out[key] = val;
620
+ }
621
+ return out;
622
+ }
623
+ /** Compatibility backfill for callers that intentionally augment process.env.
624
+ * Failed probes remain retryable; successful names are already present. */
625
+ function augmentEnvFromLoginShellSync(names, opts = {}) {
626
+ const recovered = readEnvFromLoginShellSync(names, opts);
627
+ for (const [key, value] of Object.entries(recovered)) {
628
+ if (value && !process.env[key])
629
+ process.env[key] = value;
608
630
  }
609
631
  }
610
632
  /** Startup backfill for the built-in provider auth variables. */
@@ -647,5 +669,4 @@ function __resetPathResolverForTest() {
647
669
  diagnostic = { pathSegments: [], pathSources: [], loginShellStatus: 'skipped' };
648
670
  warnedLoginShell = false;
649
671
  bundledRuntimesActive = false;
650
- loginShellEnvProbeCache.clear();
651
672
  }
@@ -4,21 +4,37 @@ exports.resolveWorktreeEnvPassthrough = resolveWorktreeEnvPassthrough;
4
4
  exports.applyWorktreeEnvPassthrough = applyWorktreeEnvPassthrough;
5
5
  const db_1 = require("./db");
6
6
  const path_resolver_1 = require("./path-resolver");
7
+ // Values stay in the owning project's memory, never process.env or SQLite.
8
+ // Both successful and missing lookups expire so profiles can recover/rotate.
9
+ const LOGIN_SHELL_CACHE_MS = 30_000;
10
+ const shellValues = new WeakMap();
11
+ function recoverProjectEnv(db, names) {
12
+ const missing = names.filter(name => !process.env[name]);
13
+ if (!missing.length) {
14
+ shellValues.delete(db);
15
+ return {};
16
+ }
17
+ const identity = JSON.stringify([names, missing, process.env.SHELL, process.env.HOME, process.env.ZDOTDIR]);
18
+ const cached = shellValues.get(db);
19
+ if (cached?.identity === identity && Date.now() < cached.expiresAt)
20
+ return cached.env;
21
+ const env = (0, path_resolver_1.readEnvFromLoginShellSync)(missing);
22
+ shellValues.set(db, { identity, expiresAt: Date.now() + LOGIN_SHELL_CACHE_MS, env });
23
+ return env;
24
+ }
7
25
  /** Resolve the per-project env passthrough overlay for a spawn.
8
26
  *
9
27
  * The project setting stores names only. Values are read from `sourceEnv` at
10
28
  * spawn time so credentials can rotate without touching SQLite. When the
11
- * source is `process.env`, missing configured names get one bounded login-shell
12
- * recovery attempt first; unresolved names are still ignored instead of
13
- * blocking the run.
29
+ * source is `process.env`, missing configured names get bounded login-shell
30
+ * recovery with a short, project-owned cache. Unresolved names remain absent.
14
31
  */
15
32
  function resolveWorktreeEnvPassthrough(db, sourceEnv = process.env) {
16
33
  const out = {};
17
34
  const names = (0, db_1.getProjectSettings)(db).worktreeEnvPassthrough;
18
- if (sourceEnv === process.env)
19
- (0, path_resolver_1.augmentEnvFromLoginShellSync)(names);
35
+ const recovered = sourceEnv === process.env ? recoverProjectEnv(db, names) : {};
20
36
  for (const name of names) {
21
- const value = sourceEnv[name];
37
+ const value = sourceEnv[name] || recovered[name] || sourceEnv[name];
22
38
  if (value !== undefined)
23
39
  out[name] = value;
24
40
  }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkoutSubdirectory = checkoutSubdirectory;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ /** Where a directory sits inside its Git checkout. Handles worktree .git files
7
+ * as well as directories; root and non-Git directories both return ''. */
8
+ function checkoutSubdirectory(directory) {
9
+ let start;
10
+ try {
11
+ start = (0, node_fs_1.realpathSync)(directory);
12
+ }
13
+ catch {
14
+ return '';
15
+ }
16
+ for (let current = start;; current = (0, node_path_1.dirname)(current)) {
17
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(current, '.git')))
18
+ return (0, node_path_1.relative)(current, start).split(node_path_1.sep).join('/');
19
+ if ((0, node_path_1.dirname)(current) === current)
20
+ return '';
21
+ }
22
+ }
@@ -52,6 +52,7 @@ exports.linkNodeModulesIntoWorktree = linkNodeModulesIntoWorktree;
52
52
  const fs = __importStar(require("fs"));
53
53
  const path = __importStar(require("path"));
54
54
  const worktree_overlay_1 = require("./worktree-overlay");
55
+ const checkout_path_1 = require("./util/checkout-path");
55
56
  /** Package-dir discovery depth: 0 = repo root, 1 = `client/`-style children. */
56
57
  const MAX_DEPTH = 2;
57
58
  /** Name of the dependency directory this module links. */
@@ -122,24 +123,43 @@ function resolveRealPath(target) {
122
123
  return path.resolve(target);
123
124
  }
124
125
  }
126
+ /** Destination ancestors must be local directories, never links into another
127
+ * checkout. Missing directories can be created during preparation. */
128
+ function hasLocalParents(worktreePath, rel) {
129
+ let parent = worktreePath;
130
+ for (const segment of rel.split('/').slice(0, -1)) {
131
+ parent = path.join(parent, segment);
132
+ try {
133
+ if (!fs.lstatSync(parent).isDirectory())
134
+ return false;
135
+ }
136
+ catch (error) {
137
+ if (error.code !== 'ENOENT')
138
+ return false;
139
+ }
140
+ }
141
+ return true;
142
+ }
125
143
  /**
126
144
  * Prove one worktree-relative path is an app-created warm-dependency link and
127
145
  * fingerprint it. Returns null for anything that is not EXACTLY a symlink whose
128
- * target resolves to the base checkout's identically-named directory — a real
146
+ * target resolves to the registered source's corresponding entry — a real
129
147
  * directory, a dereferencing copy, a dangling link, or a link into some other
130
148
  * tree are all unauthorized and must keep preserving the worktree.
131
149
  */
132
- function authenticateWarmLink(baseRepo, worktreePath, rel) {
150
+ function authenticateWarmLink(baseRepo, worktreePath, rel, sourceRel = rel) {
151
+ if (!hasLocalParents(worktreePath, rel))
152
+ return null;
133
153
  const dest = path.join(worktreePath, ...rel.split('/'));
134
154
  try {
135
155
  if (!fs.lstatSync(dest).isSymbolicLink())
136
156
  return null;
137
157
  const target = path.resolve(path.dirname(dest), fs.readlinkSync(dest));
138
- const expected = path.join(baseRepo, ...rel.split('/'));
158
+ const expected = path.join(baseRepo, ...sourceRel.split('/'));
139
159
  if (resolveRealPath(target) !== resolveRealPath(expected))
140
160
  return null;
141
- // A link to something that is not a live directory proves nothing about the
142
- // base checkout's dependency tree.
161
+ // A dangling link proves nothing about the source dependency tree (entries
162
+ // include both package directories and files such as Yarn's install state).
143
163
  if (!exists(target))
144
164
  return null;
145
165
  }
@@ -195,39 +215,49 @@ function discoverWorktreeDependencyPaths(worktreePath) {
195
215
  * — the link target anchored to a directory the app controls.
196
216
  */
197
217
  function authenticateWarmNodeModulesLinks(baseRepo, worktreePath) {
198
- const evidence = [];
199
- for (const rel of discoverWorktreeDependencyPaths(worktreePath)) {
200
- const legacy = authenticateWarmLink(baseRepo, worktreePath, rel);
201
- if (legacy) {
202
- evidence.push(legacy);
218
+ const evidence = new Map();
219
+ const prefix = (0, checkout_path_1.checkoutSubdirectory)(baseRepo);
220
+ // Retained mounts may still contain the former misplaced layout. Keep its
221
+ // exact source proof, without moving it or broadening cleanup to directories.
222
+ for (const destinationPrefix of prefix ? [prefix, ''] : ['']) {
223
+ const project = (rel) => destinationPrefix ? `${destinationPrefix}/${rel}` : rel;
224
+ if (!hasLocalParents(worktreePath, project(DEPS_DIR)))
203
225
  continue;
204
- }
205
- const dest = path.join(worktreePath, rel);
206
- try {
207
- if (!fs.lstatSync(dest).isDirectory())
226
+ const destinationRoot = path.join(worktreePath, destinationPrefix);
227
+ const authenticate = (sourceRel) => authenticateWarmLink(baseRepo, worktreePath, project(sourceRel), sourceRel);
228
+ for (const rel of discoverWorktreeDependencyPaths(destinationRoot)) {
229
+ const legacy = authenticate(rel);
230
+ if (legacy) {
231
+ evidence.set(legacy.path, legacy);
208
232
  continue;
209
- for (const name of fs.readdirSync(dest)) {
210
- if (exports.WORKTREE_DEPENDENCY_CACHES.includes(name))
211
- continue;
212
- const entryRel = `${rel}/${name}`;
213
- const entry = authenticateWarmLink(baseRepo, worktreePath, entryRel);
214
- if (entry) {
215
- evidence.push(entry);
233
+ }
234
+ const dest = path.join(destinationRoot, rel);
235
+ try {
236
+ if (!fs.lstatSync(dest).isDirectory())
216
237
  continue;
217
- }
218
- // Scoped package parents are local too; never follow foreign symlinks.
219
- if (name.startsWith('@') && fs.lstatSync(path.join(dest, name)).isDirectory()) {
220
- for (const scoped of fs.readdirSync(path.join(dest, name))) {
221
- const child = authenticateWarmLink(baseRepo, worktreePath, `${entryRel}/${scoped}`);
222
- if (child)
223
- evidence.push(child);
238
+ for (const name of fs.readdirSync(dest)) {
239
+ if (exports.WORKTREE_DEPENDENCY_CACHES.includes(name))
240
+ continue;
241
+ const entryRel = `${rel}/${name}`;
242
+ const entry = authenticate(entryRel);
243
+ if (entry) {
244
+ evidence.set(entry.path, entry);
245
+ continue;
246
+ }
247
+ // Scoped package parents are local too; never follow foreign symlinks.
248
+ if (name.startsWith('@') && fs.lstatSync(path.join(dest, name)).isDirectory()) {
249
+ for (const scoped of fs.readdirSync(path.join(dest, name))) {
250
+ const child = authenticate(`${entryRel}/${scoped}`);
251
+ if (child)
252
+ evidence.set(child.path, child);
253
+ }
224
254
  }
225
255
  }
226
256
  }
257
+ catch { /* Unreadable or concurrently removed entries confer no authority. */ }
227
258
  }
228
- catch { /* Unreadable or concurrently removed entries confer no authority. */ }
229
259
  }
230
- return evidence;
260
+ return [...evidence.values()];
231
261
  }
232
262
  function linkDependencyEntry(source, destination) {
233
263
  fs.symlinkSync(source, destination, process.platform === 'win32'
@@ -262,13 +292,19 @@ function linkNodeModulesIntoWorktree(baseRepo, worktreePath) {
262
292
  const result = { linked: [], authenticated: [], evidence: [], warnings: [] };
263
293
  if (!isWorktreeNodeModulesEnabled())
264
294
  return result;
295
+ const prefix = (0, checkout_path_1.checkoutSubdirectory)(baseRepo);
265
296
  for (const pkgRel of discoverPackageDirs(baseRepo)) {
266
- const rel = pkgRel === '' ? DEPS_DIR : `${pkgRel}/${DEPS_DIR}`;
267
- const src = path.join(baseRepo, ...rel.split('/'));
297
+ const sourceRel = pkgRel === '' ? DEPS_DIR : `${pkgRel}/${DEPS_DIR}`;
298
+ const rel = prefix ? `${prefix}/${sourceRel}` : sourceRel;
299
+ const src = path.join(baseRepo, ...sourceRel.split('/'));
268
300
  if (!isDir(src))
269
301
  continue;
302
+ if (!hasLocalParents(worktreePath, rel)) {
303
+ result.warnings.push(`failed to prepare ${rel}: destination parent is not a local directory`);
304
+ continue;
305
+ }
270
306
  const dest = path.join(worktreePath, ...rel.split('/'));
271
- const legacy = authenticateWarmLink(baseRepo, worktreePath, rel);
307
+ const legacy = authenticateWarmLink(baseRepo, worktreePath, rel, sourceRel);
272
308
  if (!exists(dest) || legacy) {
273
309
  let staging;
274
310
  let removedLegacy = false;
@@ -280,7 +316,7 @@ function linkNodeModulesIntoWorktree(baseRepo, worktreePath) {
280
316
  if (legacy) {
281
317
  // Recheck immediately before removing the old link. Never unlink a
282
318
  // directory or a replacement another actor installed in the meantime.
283
- const current = authenticateWarmLink(baseRepo, worktreePath, rel);
319
+ const current = authenticateWarmLink(baseRepo, worktreePath, rel, sourceRel);
284
320
  if (!current || current.digest !== legacy.digest)
285
321
  throw new Error('dependency link changed during preparation');
286
322
  fs.unlinkSync(dest);
@@ -1 +0,0 @@
1
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{t as r}from"./react-dom-DiWzBDP9.js";import{a as i,c as a,l as o,m as s}from"./useSharedWebSocket-C17TGqT8.js";import{n as c}from"./createLucideIcon-Bjbbp_KS.js";import{a as l,s as u}from"./useDesktop-DpxVGfUC.js";import{dt as d,gt as f}from"./TicketDetailModalContext-CkVXuHxU.js";import{a as p,i as m,n as h,o as g,r as _,s as v,t as ee}from"./InteractiveJobComposer-Da9dvM-0.js";import{t as te}from"./loader-circle-DcygOQpC.js";import{t as ne}from"./x-CBMI9ejn.js";import{F as re,I as ie,P as ae}from"./dialog-CMgps2MY.js";import{t as oe}from"./formatDistanceToNow-DlQaXc7c.js";import{t as y}from"./button-D1xExSTF.js";import{G as se,J as b,W as x}from"./index-Czov72g7.js";import{n as S,t as C}from"./job-time-BCL7wee9.js";var ce=e(r(),1),w=e(t(),1),T=[1e3,2e3,4e3,8e3,16e3];function E(e,t){let[n,r]=(0,w.useState)(`connecting`),o=(0,w.useRef)(t);return o.current=t,(0,w.useEffect)(()=>{let t=!1,n=!1,s=null,c=0,l=null;async function u(){if(!(t||n||s)){n=!0;try{await a()}catch{}finally{n=!1}!t&&!s&&f()}}function d(){if(t)return;l!==null&&clearTimeout(l),r(`connecting`);let e=T[c++]??3e4;l=setTimeout(()=>{l=null,u()},e)}function f(){if(t||s)return;let n=i(),a;try{a=n?new WebSocket(e,[`specrails-desktop`,n]):new WebSocket(e)}catch{d();return}s=a,r(`connecting`),a.onopen=()=>{t||s!==a||(c=0,r(`connected`))},a.onmessage=e=>{if(!(t||s!==a))try{o.current(JSON.parse(e.data))}catch{}},a.onclose=()=>{t||s!==a||(s=null,d())}}let p=()=>{t||s||(l!==null&&clearTimeout(l),l=null,u())};return window.addEventListener(`online`,p),window.addEventListener(`focus`,p),f(),()=>{t=!0,window.removeEventListener(`online`,p),window.removeEventListener(`focus`,p),l!==null&&clearTimeout(l);let e=s;if(s=null,e){e.onopen=null,e.onmessage=null,e.onclose=null,e.onerror=null;try{e.close()}catch{}}}},[e]),{connectionStatus:n}}var D=n();function O({jobId:e,onClose:t,projectId:n}){let[r,i]=(0,w.useState)(e);(0,w.useEffect)(()=>{i(e)},[e]);let{t:a}=c(`jobs`),{t:T}=c(`narration`),O=(0,w.useCallback)(()=>n?`${o}/api/projects/${n}`:l(),[n]),[k,le]=(0,w.useState)(h),ue=(0,w.useCallback)(e=>{le(e),_(e)},[]),[A,j]=(0,w.useState)(null),[M,N]=(0,w.useState)([]),[P,F]=(0,w.useState)([]),[I,L]=(0,w.useState)({}),[R,z]=(0,w.useState)(!0),[B,V]=(0,w.useState)(!1),[H,U]=(0,w.useState)(!1),[W,G]=(0,w.useState)(!1);(0,w.useEffect)(()=>{async function e(){try{let e=await fetch(`${O()}/jobs/${r}`);if(e.status===404){V(!0);return}if(!e.ok)throw Error(`Failed to fetch job`);let t=await e.json();if(j(t.job),N(t.events),t.phaseDefinitions){F(t.phaseDefinitions);let e={};for(let n of t.phaseDefinitions)e[n.key]=`idle`;L(e)}}catch{V(!0)}finally{z(!1)}}e()},[r,O]);let K=(0,w.useCallback)(()=>{fetch(`${O()}/jobs/${r}`).then(e=>e.ok?e.json():null).then(e=>{e?.job&&j(e.job)}).catch(()=>{})},[r,O]),q=(0,w.useRef)([]),J=(0,w.useRef)(null),Y=(0,w.useCallback)(()=>{J.current=null;let e=q.current;e.length!==0&&(q.current=[],N(t=>{let n=[...t,...e];return n.length>1e4?n.slice(n.length-8e3):n}))},[]);(0,w.useEffect)(()=>()=>{J.current&&cancelAnimationFrame(J.current)},[]),E(f,(0,w.useCallback)(e=>{let t=e;if(t.type===`event`&&t.jobId===r){let e={id:Date.now(),job_id:r,seq:0,event_type:t.event_type,source:t.source,payload:t.payload,timestamp:t.timestamp};q.current.push(e),J.current||=requestAnimationFrame(Y)}else if(t.type===`log`&&t.processId===r){let e={id:Date.now(),job_id:r,seq:0,event_type:`log`,source:t.source,payload:JSON.stringify({line:t.line}),timestamp:t.timestamp};q.current.push(e),J.current||=requestAnimationFrame(Y)}else if(t.type===`job.finalized`&&t.jobId===r||t.type===`runtime.continuation`&&t.jobId===r||t.type===`job.interactive`&&t.jobId===r||(t.type===`loop.run_paused`||t.type===`loop.run_resumed`)&&t.loopRunId===r)K();else if(t.type===`phase`){let e=t.phase,n=t.state;L(t=>({...t,[e]:n}))}else if(t.type===`queue`){let e=t.jobs?.find(e=>e.id===r);e&&A&&j(t=>t&&{...t,status:e.status})}},[r,A,Y,K])),(0,w.useEffect)(()=>{function e(e){if(!(e.key!==`Escape`||e.defaultPrevented)){if(H){U(!1);return}t()}}return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[t,H]);let X=A?se(A):`job`,Z=X===`loop-run`;async function de(){G(!0);try{let e=await x({projectId:n??null,jobId:r,kind:X});e.ok?(u.success(a(Z?`modal.toast.stopSignalSent`:`modal.toast.cancelSignalSent`),{description:a(Z?`modal.toast.stopSignalSentDescription`:`modal.toast.cancelSignalSentDescription`)}),K()):u.error(a(`modal.toast.cancelFailed`),{description:e.error})}finally{G(!1)}}let fe=A?.status===`running`,Q=A?S(C(A)):null,{panelRef:pe,panelStyle:$,headerHandleProps:me,resizeHandles:he,isFloating:ge,guardBackdrop:_e}=re();return(0,ce.createPortal)((0,D.jsx)(b,{delayDuration:400,children:(0,D.jsxs)(`div`,{style:ie(),className:`fixed inset-0 z-[65] flex items-stretch justify-center p-[clamp(12px,2vmin,24px)]`,children:[(0,D.jsx)(`div`,{className:`absolute inset-0 bg-black/50 backdrop-blur-sm`,onClick:_e(t)}),(0,D.jsxs)(`div`,{ref:pe,className:`relative w-full min-w-0 rounded-xl glass-card border border-border/30 flex flex-col animate-in fade-in zoom-in-95 duration-200 overflow-hidden`,style:$,children:[(0,D.jsxs)(`div`,{...me,className:`flex items-center justify-between px-4 py-3 border-b border-border/30${ge?` cursor-grab active:cursor-grabbing`:``}`,children:[(0,D.jsxs)(`div`,{className:`flex items-center gap-3 min-w-0`,children:[A&&(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(`code`,{className:`text-xs font-mono text-foreground/80 truncate`,children:A.command}),(0,D.jsx)(`span`,{className:`text-[10px] text-muted-foreground shrink-0`,children:Q?oe(Q,{addSuffix:!0,locale:s()}):`—`})]}),R&&(0,D.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:a(`common:states.loading`)})]}),(0,D.jsx)(`button`,{onClick:t,className:`h-7 w-7 shrink-0 flex items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-surface/50 transition-colors cursor-pointer`,children:(0,D.jsx)(ne,{className:`w-4 h-4`})})]}),A&&(0,D.jsx)(v,{onOpenRun:i,job:A,events:M,phases:I,phaseDefinitions:P,projectId:n,variant:`glass`,actions:fe?(0,D.jsxs)(y,{variant:`ghost`,size:`sm`,onClick:()=>U(!0),disabled:W,className:`h-7 text-destructive hover:text-destructive hover:bg-destructive/10`,children:[W&&(0,D.jsx)(te,{className:`w-3.5 h-3.5 mr-1.5 animate-spin`,"aria-hidden":!0}),a(Z?`dashboard:railControls.stop`:X===`interactive`?`common:actions.discard`:`common:actions.cancel`)]}):null}),(0,D.jsx)(`div`,{className:`min-h-0 flex-1 overflow-hidden relative`,children:B?(0,D.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,D.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:a(`modal.notFound`)})}):d&&A?(0,D.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col`,children:[(0,D.jsx)(`div`,{className:`flex items-center gap-1 border-b border-border/40 px-3 py-1.5`,children:[`narrated`,`log`].map(e=>(0,D.jsx)(`button`,{type:`button`,onClick:()=>ue(e),"aria-pressed":k===e,"data-testid":`modal-narration-mode-${e}`,className:`rounded-md px-2 py-0.5 text-[11px] font-medium transition-colors ${k===e?`bg-accent-primary/15 text-accent-primary`:`text-muted-foreground hover:bg-surface/60`}`,children:T(e===`narrated`?`modeNarrated`:`modeLog`)},e))}),(0,D.jsx)(`div`,{className:`min-h-0 flex-1 overflow-hidden`,children:k===`narrated`?(0,D.jsx)(m,{events:M,settled:A.status!==`running`&&A.status!==`queued`,elapsedMs:A.duration_ms??null,variant:`glass`}):A.command.startsWith(`loop:`)?(0,D.jsx)(p,{events:M,jobStatus:A.status,isLoading:R,variant:`glass`,projectId:n,onOpenRun:i}):(0,D.jsx)(g,{events:M,isLoading:R,projectId:n})})]}):A?.command.startsWith(`loop:`)?(0,D.jsx)(p,{events:M,jobStatus:A.status,isLoading:R,variant:`glass`,projectId:n,onOpenRun:i}):(0,D.jsx)(g,{events:M,isLoading:R,projectId:n})}),!!A?.interactive&&A.status===`running`&&(0,D.jsx)(ee,{jobId:r,projectId:n,settleMode:A.interactiveSettleMode,initialAcceptingTurns:A.interactiveAcceptingTurns,pendingInterrupts:A.pendingInterrupts,kind:A.command.startsWith(`loop:`)?`loop-step`:`job`,variant:`glass`,onFinalized:K})]}),(0,D.jsx)(ae,{handles:he}),H&&(0,D.jsxs)(`div`,{"data-testid":`job-cancel-confirm`,className:`absolute inset-0 z-10 flex items-center justify-center p-4`,children:[(0,D.jsx)(`div`,{className:`absolute inset-0 bg-black/60 backdrop-blur-sm`,onClick:()=>U(!1)}),(0,D.jsxs)(`div`,{className:`relative w-full max-w-sm rounded-xl glass-card border border-border/30 p-5 shadow-xl animate-in fade-in zoom-in-95 duration-150`,children:[(0,D.jsx)(`h3`,{className:`text-base font-semibold text-foreground`,children:a(Z?`modal.stopRunConfirmTitle`:`modal.cancelConfirmTitle`)}),(0,D.jsx)(`p`,{className:`mt-1.5 text-sm text-muted-foreground`,children:a(Z?`modal.stopRunConfirmDescription`:`modal.cancelConfirmDescription`)}),(0,D.jsxs)(`div`,{className:`mt-4 flex justify-end gap-2`,children:[(0,D.jsx)(y,{variant:`ghost`,size:`sm`,onClick:()=>U(!1),children:a(`modal.keepRunning`)}),(0,D.jsx)(y,{variant:`destructive`,size:`sm`,onClick:()=>{U(!1),de()},children:a(Z?`modal.stopRun`:X===`interactive`?`common:actions.discard`:`modal.cancelJob`)})]})]})]})]})}),document.body)}export{O as JobDetailModal};
@@ -1 +0,0 @@
1
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{d as r,n as i,u as a}from"./chunk-KS7C4IRE-DEKu087k.js";import{m as o,r as ee}from"./useSharedWebSocket-C17TGqT8.js";import{n as s}from"./createLucideIcon-Bjbbp_KS.js";import{a as c,r as te,s as l}from"./useDesktop-DpxVGfUC.js";import{dt as u,n as ne}from"./TicketDetailModalContext-CkVXuHxU.js";import{n as d}from"./save-OsUqVn99.js";import{c as f}from"./MinimizedChatsContext-DKlPf54Y.js";import{n as re}from"./file-text-BcLCfcWZ.js";import{a as p,c as ie,i as ae,n as oe,o as m,r as se,s as ce,t as le}from"./InteractiveJobComposer-Da9dvM-0.js";import{t as ue}from"./download-CFHyyHcw.js";import{t as de}from"./rotate-ccw-Wh4id13q.js";import{t as h}from"./utils-BC4DHlz2.js";import{t as fe}from"./formatDistanceToNow-DlQaXc7c.js";import{t as g}from"./button-D1xExSTF.js";import{K as _,Nt as v,W as pe,Y as y,q as b}from"./index-Czov72g7.js";import{t as me}from"./format-command-C0D9IPmV.js";var x=e(t(),1),S=n(),C=4;function he({tickets:e,onTicketClick:t}){let{t:n}=s(`jobs`),[r,i]=(0,x.useState)(!1);if(e.length===0)return null;let a=e.length>=C&&!r?e.slice(0,1):e,o=e.length-a.length;return(0,S.jsxs)(`div`,{className:`rounded-xl border border-border/40 bg-card/40 px-4 py-3 space-y-1.5`,children:[a.map(e=>(0,S.jsx)(w,{ticket:e,onClick:()=>t(e.id)},e.id)),o>0&&(0,S.jsxs)(`button`,{type:`button`,onClick:()=>i(!0),className:`flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors`,children:[n(`ticketHeader.showMore`,{count:o}),(0,S.jsx)(d,{className:`w-3 h-3`})]}),r&&e.length>=C&&(0,S.jsxs)(`button`,{type:`button`,onClick:()=>i(!1),className:`flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors`,children:[n(`ticketHeader.showLess`),(0,S.jsx)(f,{className:`w-3 h-3`})]})]})}function w({ticket:e,onClick:t}){let{t:n}=s(`jobs`),r=e.title==null,i=`inline-flex items-center px-1.5 py-0.5 rounded-md text-[11px] font-mono tabular-nums shrink-0`;return r?(0,S.jsx)(`div`,{className:`flex items-center gap-2 min-w-0`,children:(0,S.jsx)(`span`,{className:h(i,`text-muted-foreground bg-muted/20 select-none`),title:n(`ticketHeader.deletedTooltip`),children:n(`ticketHeader.deletedTicket`,{id:e.id})})}):(0,S.jsxs)(`button`,{type:`button`,onClick:t,className:`flex items-center gap-2 min-w-0 w-full text-left group`,title:e.title??void 0,children:[(0,S.jsxs)(`span`,{className:h(i,`text-accent-primary bg-accent-primary/10 group-hover:bg-accent-primary/20 transition-colors`),children:[`#`,e.id]}),(0,S.jsx)(`span`,{className:`text-lg font-semibold text-foreground truncate group-hover:text-accent-primary transition-colors`,children:e.title})]})}function T(){let{t:e}=s(`jobs`),{t}=s(`narration`),{id:n}=r(),{activeProjectId:d,projects:f}=te(),C=f.find(e=>e.id===d)?.provider,w=a(),{openTicketDetail:T}=ne(),[E,D]=(0,x.useState)(null),[O,k]=(0,x.useState)([]),[ge,A]=(0,x.useState)([]),[_e,j]=(0,x.useState)({}),[ve,M]=(0,x.useState)([]),[ye,N]=(0,x.useState)(!0),[be,P]=(0,x.useState)(!1),[F,xe]=(0,x.useState)(oe),[Se,I]=(0,x.useState)(null),Ce=(0,x.useCallback)(e=>{xe(e),se(e)},[]),L=E?.command??null;(0,x.useEffect)(()=>{if(!u||F!==`narrated`||!d||!L)return;let e=!1,t=`command=${encodeURIComponent(L)}`;try{Promise.resolve(fetch(`${c()}/run-duration-range?${t}`)).then(e=>e&&e.ok?e.json():null).then(t=>{e||I(t?.range??null)}).catch(()=>{e||I(null)})}catch{I(null)}return()=>{e=!0}},[d,L,F]);let[R,z]=(0,x.useState)(!1),B=(0,x.useRef)(!1),V=(0,x.useRef)(null);(0,x.useEffect)(()=>{B.current=!1,V.current=null,z(!1)},[n]),(0,x.useEffect)(()=>{if(!n)return;let e=new AbortController;D(null),k([]),A([]),j({}),N(!0),P(!1);async function t(){try{let t=await fetch(`${c()}/jobs/${n}`,{signal:e.signal});if(t.status===404){P(!0);return}if(!t.ok)throw Error(`Failed to fetch job`);let r=await t.json();D(r.job),k(r.events)}catch(e){if(e.name===`AbortError`)return;P(!0)}finally{e.signal.aborted||N(!1)}}return t(),()=>e.abort()},[n,d]),(0,x.useEffect)(()=>{if(!E?.pipeline_id){M([]);return}let e=E.pipeline_id;fetch(`${c()}/pipelines/${e}`).then(e=>e.ok?e.json():null).then(e=>{e?.jobs&&M(e.jobs)}).catch(()=>{})},[E?.pipeline_id,E?.status]);let H=(0,x.useRef)(d);H.current=d;let U=(0,x.useRef)(null);U.current=E?.status??null;let W=(0,x.useRef)([]),G=(0,x.useRef)(null),K=(0,x.useCallback)(()=>{G.current=null;let e=W.current;e.length!==0&&(W.current=[],k(t=>{let n=[...t,...e];return n.length>1e4?n.slice(n.length-8e3):n}))},[]),q=(0,x.useCallback)(e=>{let t=W.current;t.push(e),t.length>1e4&&(W.current=t.slice(-8e3)),G.current||=requestAnimationFrame(K)},[K]);(0,x.useEffect)(()=>()=>{G.current&&cancelAnimationFrame(G.current)},[]);let J=(0,x.useCallback)(e=>{let t=e;if(!(H.current&&t.projectId&&t.projectId!==H.current)){if(t.type===`init`){let e=t.phaseDefinitions??[];A(e);let n={};for(let r of e)n[r.key]=t.phases?.[r.key]??`idle`;j(n)}else if(t.type===`event`&&t.jobId===n){let e={id:Date.now(),job_id:n??``,seq:t.seq??0,event_type:t.event_type,source:t.source,payload:t.payload,timestamp:t.timestamp};q(e)}else if(t.type===`log`&&t.processId===n){let e={id:Date.now(),job_id:n??``,seq:0,event_type:`log`,source:t.source,payload:JSON.stringify({line:t.line}),timestamp:t.timestamp};q(e)}else if(t.type===`job.finalized`&&t.jobId===n||t.type===`runtime.continuation`&&t.jobId===n||t.type===`job.interactive`&&t.jobId===n||(t.type===`loop.run_paused`||t.type===`loop.run_resumed`)&&t.loopRunId===n)fetch(`${c()}/jobs/${n}`).then(e=>e.json()).then(e=>D(e.job)).catch(()=>{});else if(t.type===`phase`){let e=t.phase,n=t.state;j(t=>({...t,[e]:n}))}else if(t.type===`queue`){let e=t.jobs?.find(e=>e.id===n);if(e){let t=e.status,r=U.current;D(e=>e&&{...e,status:t}),(t===`completed`||t===`failed`||t===`canceled`||t===`running`&&r!==`running`)&&fetch(`${c()}/jobs/${n}`).then(e=>e.json()).then(e=>D(e.job)).catch(()=>{})}}}},[n,q]),{registerHandler:Y,unregisterHandler:X}=ee();(0,x.useEffect)(()=>(Y(`job-detail-${n}`,J),()=>X(`job-detail-${n}`)),[n,J,Y,X]),(0,x.useEffect)(()=>{if(!n||!E||E.status!==`running`||E.hasTelemetry)return;let e=setInterval(()=>{fetch(`${c()}/jobs/${n}`).then(e=>e.ok?e.json():null).then(e=>{e?.job?.hasTelemetry&&D(e=>e&&{...e,hasTelemetry:!0})}).catch(()=>{})},8e3);return()=>clearInterval(e)},[n,E?.status,E?.hasTelemetry]);async function we(){if(!n)return;let t=await pe({projectId:null,jobId:n});t.ok?l.success(e(`detail.toast.cancelSignalSent`),{description:e(`detail.toast.cancelSignalSentDescription`)}):t.httpStatus===null?l.error(e(`detail.toast.networkError`)):l.error(e(`detail.toast.failed`),{description:t.error})}async function Te(){if(E)try{let t=await fetch(`${c()}/jobs/${E.id}/diagnostic`);if(!t.ok){let n=await t.json().catch(()=>({}));throw Error(n.error??e(`detail.toast.exportFailedHttp`,{status:t.status}))}let n=await t.blob(),r=URL.createObjectURL(n),i=new Date().toISOString().slice(0,10),a=document.createElement(`a`);a.href=r,a.download=`specrails-diagnostic-${E.id}-${i}.zip`,document.body.appendChild(a),a.click(),a.remove(),URL.revokeObjectURL(r)}catch(t){l.error(e(`detail.toast.exportFailed`),{description:t.message})}}async function Ee(){if(!(!E||B.current)){B.current=!0,z(!0),V.current||=typeof globalThis.crypto?.randomUUID==`function`?globalThis.crypto.randomUUID():`rerun-${E.id}-${Date.now()}-${Math.random().toString(36).slice(2)}`;try{let t=await fetch(`${c()}/spawn`,{method:`POST`,headers:{"Content-Type":`application/json`,"Idempotency-Key":V.current},body:JSON.stringify({rerunOfJobId:E.id})}),n=await t.json();if(!t.ok)throw Error(n.error??e(`detail.toast.spawnFailed`));V.current=null,l.success(e(`detail.toast.jobRequeued`)),w(`/jobs/${n.jobId}`)}catch(e){l.error(e.message)}finally{B.current=!1,z(!1)}}}if(ye)return(0,S.jsx)(`div`,{className:`max-w-5xl mx-auto px-4 py-6`,children:(0,S.jsxs)(`div`,{className:`space-y-3`,children:[(0,S.jsx)(`div`,{className:`h-4 w-48 bg-muted/30 rounded animate-pulse`}),(0,S.jsx)(`div`,{className:`h-20 bg-muted/30 rounded-lg animate-pulse`}),(0,S.jsx)(`div`,{className:`h-64 bg-muted/30 rounded-lg animate-pulse`})]})});if(be||!E)return(0,S.jsxs)(`div`,{className:`max-w-5xl mx-auto px-4 py-6 flex flex-col items-center gap-3 mt-12`,children:[(0,S.jsx)(`p`,{className:`text-lg font-semibold`,children:e(`detail.notFound`)}),(0,S.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`detail.notFoundDescription`,{id:n})}),(0,S.jsx)(g,{asChild:!0,variant:`outline`,size:`sm`,children:(0,S.jsxs)(i,{to:`/`,children:[(0,S.jsx)(v,{className:`w-3.5 h-3.5 mr-1.5`}),e(`detail.backToDashboard`)]})})]});let Z=E.status===`running`,De=E.status===`completed`||E.status===`failed`,Q=!!E.interactive,Oe=Q&&Z,ke=(E.tickets?.length??0)>0,$=E.command.startsWith(`loop:`),Ae=ie(ve);return(0,S.jsxs)(`div`,{"data-job-detail-surface":!0,className:`flex flex-col h-full max-w-5xl mx-auto w-full`,children:[(0,S.jsxs)(`div`,{className:`px-4 pt-3 pb-2 space-y-2`,children:[(0,S.jsxs)(`div`,{className:`flex items-center gap-1 text-xs text-muted-foreground`,children:[(0,S.jsxs)(i,{to:`/`,className:`hover:text-foreground transition-colors flex items-center gap-1`,children:[(0,S.jsx)(v,{className:`w-3 h-3`}),e(`detail.breadcrumbDashboard`)]}),(0,S.jsx)(re,{className:`w-3 h-3`}),(0,S.jsx)(`span`,{className:`text-foreground font-mono`,children:e(`detail.breadcrumbJob`,{id:n?.slice(0,8)})})]}),ke&&(0,S.jsx)(he,{tickets:E.tickets??[],onTicketClick:T}),(0,S.jsxs)(`div`,{className:`flex items-center gap-3 text-xs text-muted-foreground flex-wrap min-w-0`,children:[(0,S.jsx)(`code`,{className:h(`font-mono text-foreground/90 truncate`,ke?`text-xs text-muted-foreground`:`text-sm`),children:me(E.command,C)}),(0,S.jsx)(`span`,{children:E.started_at?e(`detail.startedAgo`,{timeAgo:fe(new Date(E.started_at),{addSuffix:!0,locale:o()})}):e(`detail.queuedWaiting`)}),E.model&&(0,S.jsx)(`span`,{className:`text-muted-foreground/40`,children:E.model})]})]}),(0,S.jsx)(ce,{onOpenRun:e=>w(`/jobs/${e}`),job:E,events:O,phases:_e,phaseDefinitions:ge,projectId:d,variant:`page`,pipelineTotals:Ae,actions:(0,S.jsxs)(S.Fragment,{children:[E.hasTelemetry&&(0,S.jsxs)(_,{children:[(0,S.jsx)(y,{asChild:!0,children:(0,S.jsxs)(g,{variant:`outline`,size:`sm`,className:`h-7`,onClick:Te,"aria-label":e(`detail.exportDiagnosticAria`),children:[(0,S.jsx)(ue,{className:`w-3.5 h-3.5 mr-1.5`}),e(`detail.exportDiagnostic`)]})}),(0,S.jsx)(b,{children:e(`detail.exportDiagnosticTooltip`)})]}),De&&(0,S.jsxs)(_,{children:[(0,S.jsx)(y,{asChild:!0,children:(0,S.jsxs)(g,{variant:`outline`,size:`sm`,onClick:Ee,disabled:R,"aria-busy":R,className:`h-7`,children:[(0,S.jsx)(de,{className:`w-3.5 h-3.5 mr-1.5`}),e(`detail.reExecute`)]})}),(0,S.jsx)(b,{children:e(`detail.reExecuteTooltip`)})]}),Z&&(0,S.jsxs)(_,{children:[(0,S.jsx)(y,{asChild:!0,children:(0,S.jsx)(g,{variant:`outline`,size:`sm`,onClick:we,className:`h-7 border-destructive/30 text-destructive hover:bg-destructive/10`,children:e(Q?`detail.cancelInteractive`:`detail.cancelJob`)})}),(0,S.jsx)(b,{children:e(`detail.cancelJobTooltip`)})]})]})}),(0,S.jsx)(`div`,{className:`flex-1 overflow-hidden relative`,children:u?(0,S.jsxs)(`div`,{className:`flex h-full flex-col`,children:[(0,S.jsx)(`div`,{className:`flex items-center gap-1 border-b border-border/50 px-3 py-1.5`,children:[`narrated`,`log`].map(e=>(0,S.jsx)(`button`,{type:`button`,onClick:()=>Ce(e),"aria-pressed":F===e,"data-testid":`job-narration-mode-${e}`,className:`rounded-md px-2 py-0.5 text-[11px] font-medium transition-colors ${F===e?`bg-accent-primary/15 text-accent-primary`:`text-muted-foreground hover:bg-surface`}`,children:t(e===`narrated`?`modeNarrated`:`modeLog`)},e))}),(0,S.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:F===`narrated`?(0,S.jsx)(ae,{events:O,settled:E.status!==`running`&&E.status!==`queued`,durationRange:Se,elapsedMs:E.duration_ms??null,variant:`page`}):$?(0,S.jsx)(p,{events:O,jobStatus:E.status,variant:`page`,onOpenRun:e=>w(`/jobs/${e}`)}):(0,S.jsx)(m,{events:O})})]}):$?(0,S.jsx)(p,{events:O,jobStatus:E.status,variant:`page`,onOpenRun:e=>w(`/jobs/${e}`)}):(0,S.jsx)(m,{events:O})}),Oe&&(0,S.jsx)(le,{jobId:E.id,settleMode:E.interactiveSettleMode,initialAcceptingTurns:E.interactiveAcceptingTurns,pendingInterrupts:E.pendingInterrupts,kind:E.command.startsWith(`loop:`)?`loop-step`:`job`,variant:`page`})]})}export{T as default};
@@ -1,2 +0,0 @@
1
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{l as r,r as i}from"./useSharedWebSocket-C17TGqT8.js";import{n as a,t as o}from"./createLucideIcon-Bjbbp_KS.js";import{n as s,r as c,s as l}from"./useDesktop-DpxVGfUC.js";import{s as u}from"./provider-capabilities-lePfeT_Y.js";import{t as d}from"./download-CFHyyHcw.js";import{t as f}from"./external-link-D6XxC_Cu.js";import{t as p}from"./loader-circle-DcygOQpC.js";import{t as m}from"./search-Cq_gE3Ik.js";import{t as h}from"./trash-2-BOU3WdZZ.js";import{t as g}from"./triangle-alert-H9T4jAmh.js";import{n as _,t as v}from"./jira-api-CbJvbRYx.js";import{i as y,l as b,o as x,r as S,t as C}from"./dialog-CMgps2MY.js";import{t as w}from"./utils-BC4DHlz2.js";import{t as T}from"./button-D1xExSTF.js";import{Et as E,Gt as D,Tt as O,U as k,an as A,bt as j,on as ee,qt as M,vt as te,yt as ne}from"./index-Czov72g7.js";var N=o(`earth`,[[`path`,{d:`M21.54 15H17a2 2 0 0 0-2 2v4.54`,key:`1djwo0`}],[`path`,{d:`M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17`,key:`1tzkfa`}],[`path`,{d:`M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05`,key:`14pb5j`}],[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]),P=o(`folder-kanban`,[[`path`,{d:`M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z`,key:`1fr9dc`}],[`path`,{d:`M8 10v4`,key:`tgpxqk`}],[`path`,{d:`M12 10v2`,key:`hh53o1`}],[`path`,{d:`M16 10v6`,key:`1d6xys`}]]),re=o(`power`,[[`path`,{d:`M12 2v10`,key:`mnfbl`}],[`path`,{d:`M18.4 6.6a9 9 0 1 1-12.77.04`,key:`obofu9`}]]),F=o(`settings-2`,[[`path`,{d:`M14 17H5`,key:`gfn3mx`}],[`path`,{d:`M19 7h-9`,key:`6i9tg`}],[`circle`,{cx:`17`,cy:`17`,r:`3`,key:`18b49y`}],[`circle`,{cx:`7`,cy:`7`,r:`3`,key:`dfmy0x`}]]),I=e(t(),1),L=n(),ie=[`todo`,`in_progress`,`on_review`,`done`,`cancelled`],ae={todo:`todo`,in_progress:`inProgress`,on_review:`onReview`,done:`done`,cancelled:`cancelled`};function R({onConnected:e,onSkip:t,apiBase:n}){let{t:r}=a(`jira`),[i,o]=(0,I.useState)(1),[s,c]=(0,I.useState)(``),[u,d]=(0,I.useState)(``),[f,p]=(0,I.useState)(``),[m,h]=(0,I.useState)(`idle`),[g,_]=(0,I.useState)(null),[y,b]=(0,I.useState)(`cloud`),[x,S]=(0,I.useState)([]),[C,w]=(0,I.useState)(``),[E,D]=(0,I.useState)(``),[O,A]=(0,I.useState)(``),[j,ee]=(0,I.useState)(!1),[M,te]=(0,I.useState)([]),[ne,N]=(0,I.useState)({}),[P,re]=(0,I.useState)(``),[F,R]=(0,I.useState)(!1),H=()=>({baseUrl:s.trim(),accountEmail:u.trim()||null,token:f});async function U(){h(`testing`);try{let e=await v.test(H(),n);h(`ok`),_(e.displayName),b(e.deployment)}catch(e){h(`error`),l.error(V(e,r))}}async function W(){o(2),ee(!0);try{let{projects:e}=await v.discoverProjects({...H(),query:C.trim()||void 0},n);S(e)}catch(e){l.error(V(e,r))}finally{ee(!1)}}async function oe(){if(o(3),E)try{let{statuses:e}=await v.discoverStatuses({...H(),projectKey:E},n);te(e)}catch{te([])}}async function G(){R(!0);try{let t=Object.fromEntries(Object.entries(ne).filter(([,e])=>e));await v.connect({...H(),jiraProjectKey:E.trim(),statusMap:Object.keys(t).length?t:null,discardStatus:P.trim()||null},n),l.success(r(`status.connected`,{key:E.trim()})),e()}catch(e){l.error(V(e,r))}finally{R(!1)}}return(0,L.jsxs)(`div`,{className:`space-y-4`,"data-testid":`jira-wizard`,children:[(0,L.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,L.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:r(`wizard.step`,{current:i,total:4})}),t&&(0,L.jsx)(`button`,{type:`button`,onClick:t,className:`text-xs text-muted-foreground hover:text-foreground hover:underline`,"data-testid":`jira-later`,children:r(`wizard.later`)})]}),i===1&&(0,L.jsxs)(`div`,{className:`space-y-3`,children:[(0,L.jsx)(`h3`,{className:`text-sm font-semibold`,children:r(`wizard.step1Title`)}),(0,L.jsx)(z,{label:r(`creds.baseUrlLabel`),help:r(`creds.baseUrlHelp`),children:(0,L.jsx)(k,{value:s,onChange:e=>{c(e.target.value),h(`idle`)},placeholder:r(`creds.baseUrlPlaceholder`)})}),(0,L.jsx)(z,{label:r(`creds.emailLabel`),help:r(`creds.emailHelp`),children:(0,L.jsx)(k,{value:u,onChange:e=>{d(e.target.value),h(`idle`)},placeholder:r(`creds.emailPlaceholder`)})}),(0,L.jsx)(z,{label:r(`creds.tokenLabel`),help:r(`creds.tokenHelp`),children:(0,L.jsx)(k,{type:`password`,value:f,onChange:e=>{p(e.target.value),h(`idle`)}})}),(0,L.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,L.jsx)(T,{size:`sm`,variant:`outline`,onClick:U,disabled:!s.trim()||!f.trim()||m===`testing`,children:r(m===`testing`?`creds.testing`:m===`ok`?`creds.retest`:`creds.test`)}),m===`ok`&&(0,L.jsxs)(`span`,{className:`text-xs text-accent-success`,"data-testid":`jira-test-ok`,children:[`✓ `,r(`creds.testOk`,{name:g??``}),` · `,r(y===`cloud`?`review.cloud`:`review.dc`)]})]}),(0,L.jsx)(`div`,{className:`flex justify-end`,children:(0,L.jsx)(T,{size:`sm`,onClick:W,disabled:m!==`ok`,children:r(`wizard.next`)})})]}),i===2&&(0,L.jsxs)(`div`,{className:`space-y-3`,children:[(0,L.jsx)(`h3`,{className:`text-sm font-semibold`,children:r(`wizard.step2Title`)}),(0,L.jsxs)(`div`,{className:`flex gap-2`,children:[(0,L.jsx)(k,{value:C,onChange:e=>w(e.target.value),placeholder:r(`project.searchPlaceholder`)}),(0,L.jsx)(T,{size:`sm`,variant:`outline`,onClick:W,children:r(`project.searchPlaceholder`)})]}),j?(0,L.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:r(`project.loading`)}):x.length===0?(0,L.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:r(`project.none`)}):(0,L.jsx)(`div`,{className:`max-h-48 space-y-1 overflow-y-auto`,"data-testid":`jira-project-list`,children:x.map(e=>(0,L.jsxs)(`button`,{type:`button`,onClick:()=>{D(e.key),A(e.name)},className:`flex w-full items-center justify-between rounded px-2 py-1.5 text-left text-sm hover:bg-muted ${E===e.key?`bg-muted`:``}`,children:[(0,L.jsx)(`span`,{className:`font-medium`,children:e.key}),(0,L.jsx)(`span`,{className:`truncate text-xs text-muted-foreground`,children:e.name})]},e.id))}),(0,L.jsx)(z,{label:r(`project.manualLabel`),children:(0,L.jsx)(k,{value:E,onChange:e=>{D(e.target.value.toUpperCase()),A(``)},placeholder:r(`project.manualPlaceholder`)})}),(0,L.jsxs)(`div`,{className:`flex justify-between`,children:[(0,L.jsx)(T,{size:`sm`,variant:`ghost`,onClick:()=>o(1),children:r(`wizard.back`)}),(0,L.jsx)(T,{size:`sm`,onClick:oe,disabled:!E.trim(),children:r(`wizard.next`)})]})]}),i===3&&(0,L.jsxs)(`div`,{className:`space-y-3`,children:[(0,L.jsx)(`h3`,{className:`text-sm font-semibold`,children:r(`wizard.step3Title`)}),(0,L.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:r(`mapping.intro`)}),ie.map(e=>(0,L.jsx)(z,{label:r(`mapping.${ae[e]}`),children:(0,L.jsxs)(`select`,{value:ne[e]??``,onChange:t=>N(n=>({...n,[e]:t.target.value||void 0})),className:`w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm`,children:[(0,L.jsx)(`option`,{value:``,children:r(`mapping.auto`)}),M.map(e=>(0,L.jsx)(`option`,{value:e.name,children:e.name},e.id))]})},e)),(0,L.jsx)(z,{label:r(`discard.configLabel`),help:r(`discard.configHelp`),children:(0,L.jsxs)(`select`,{value:P,onChange:e=>re(e.target.value),className:`w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm`,"data-testid":`jira-discard-status-select`,children:[(0,L.jsx)(`option`,{value:``,children:r(`discard.configNone`)}),M.map(e=>(0,L.jsx)(`option`,{value:e.name,children:e.name},e.id))]})}),(0,L.jsxs)(`div`,{className:`flex justify-between`,children:[(0,L.jsx)(T,{size:`sm`,variant:`ghost`,onClick:()=>o(2),children:r(`wizard.back`)}),(0,L.jsx)(T,{size:`sm`,onClick:()=>o(4),children:r(`wizard.next`)})]})]}),i===4&&(0,L.jsxs)(`div`,{className:`space-y-3`,children:[(0,L.jsx)(`h3`,{className:`text-sm font-semibold`,children:r(`wizard.step4Title`)}),(0,L.jsxs)(`dl`,{className:`space-y-1 text-sm`,children:[(0,L.jsx)(B,{label:r(`review.site`),value:s}),(0,L.jsx)(B,{label:r(`review.project`),value:O?`${E} — ${O}`:E}),(0,L.jsx)(B,{label:r(`review.account`),value:g??u}),(0,L.jsx)(B,{label:r(`review.type`),value:r(y===`cloud`?`review.cloud`:`review.dc`)}),(0,L.jsx)(B,{label:r(`discard.reviewLabel`),value:P||r(`discard.configNone`)})]}),(0,L.jsxs)(`div`,{className:`flex justify-between`,children:[(0,L.jsx)(T,{size:`sm`,variant:`ghost`,onClick:()=>o(3),children:r(`wizard.back`)}),(0,L.jsx)(T,{size:`sm`,onClick:G,disabled:F,children:r(F?`wizard.connecting`:`wizard.connect`)})]})]})]})}function z({label:e,help:t,children:n}){return(0,L.jsxs)(`label`,{className:`block space-y-1`,children:[(0,L.jsx)(`span`,{className:`text-xs font-medium text-foreground`,children:e}),n,t&&(0,L.jsx)(`span`,{className:`block text-[11px] text-muted-foreground`,children:t})]})}function B({label:e,value:t}){return(0,L.jsxs)(`div`,{className:`flex justify-between gap-4`,children:[(0,L.jsx)(`dt`,{className:`text-muted-foreground`,children:e}),(0,L.jsx)(`dd`,{className:`truncate text-right font-medium`,children:t})]})}function V(e,t){return e instanceof Error?e.message:t(`errors.generic`)}var H=[`todo`,`in_progress`,`on_review`,`done`,`cancelled`],U={todo:`todo`,in_progress:`inProgress`,on_review:`onReview`,done:`done`,cancelled:`cancelled`};function W({state:e,onChanged:t,apiBase:n}){let{t:r}=a(`jira`),i=e.connection,[o,s]=(0,I.useState)(i.enabled),[c,u]=(0,I.useState)(!1),[d,f]=(0,I.useState)(!1),[p,m]=(0,I.useState)([]),[h,g]=(0,I.useState)([]),[_,y]=(0,I.useState)(i.discardStatus??``),[b,x]=(0,I.useState)(i.statusMap??{}),S=e.outbox??{pending:0,inflight:0,done:0,dead:0},C=(0,I.useCallback)(async()=>{try{let{ops:e}=n?await v.listOutbox(`dead`,n):await v.listOutbox(`dead`);m(e)}catch{m([])}},[n]);(0,I.useEffect)(()=>{C()},[C]),(0,I.useEffect)(()=>{let e=!1;return v.listStatuses(n).then(({statuses:t})=>{e||g(t)}).catch(()=>{e||g([])}),()=>{e=!0}},[n]);async function w(e){let i=_;y(e);try{n?await v.patchConnection({discardStatus:e||null},n):await v.patchConnection({discardStatus:e||null}),t()}catch(e){y(i),l.error(G(e,r))}}async function E(e,i){let a=b,o={...b,[e]:i||void 0};x(o);let s=Object.fromEntries(Object.entries(o).filter(([,e])=>e));try{n?await v.patchConnection({statusMap:Object.keys(s).length?s:null},n):await v.patchConnection({statusMap:Object.keys(s).length?s:null}),t()}catch(e){x(a),l.error(G(e,r))}}async function D(){let e=!o;s(e),f(!0);try{n?await v.setEnabled(e,n):await v.setEnabled(e)}catch(t){s(!e),l.error(G(t,r))}finally{f(!1)}}async function O(){u(!0);try{let e=n?await v.syncNow(n):await v.syncNow();l.success(r(`status.syncedToast`,{count:e.upserted}))}catch(e){l.error(G(e,r))}finally{u(!1)}}async function k(){if(window.confirm(r(`status.disconnectConfirm`))){f(!0);try{n?await v.disconnect(n):await v.disconnect(),l.success(r(`status.disconnectedToast`)),t()}catch(e){l.error(G(e,r))}finally{f(!1)}}}async function A(e){try{n?await v.retryOutbox(e,n):await v.retryOutbox(e),await C()}catch(e){l.error(G(e,r))}}let j=S.pending+S.inflight;return(0,L.jsxs)(`div`,{className:`space-y-4`,"data-testid":`jira-connected`,children:[(0,L.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`p`,{className:`text-sm font-medium text-foreground`,children:r(`status.connected`,{key:i.jiraProjectKey})}),(0,L.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[i.baseUrl,` · `,i.deployment===`cloud`?r(`review.cloud`):r(`review.dc`)]})]}),(0,L.jsx)(`a`,{href:`${i.baseUrl}/browse/${i.jiraProjectKey}`,target:`_blank`,rel:`noreferrer`,className:`text-xs text-accent-primary hover:underline`,children:r(`badge.openInJira`)})]}),(0,L.jsxs)(`div`,{className:`flex items-center justify-between rounded-md border border-border p-3`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`p`,{className:`text-sm font-medium`,children:r(`status.enabledLabel`)}),(0,L.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:r(o?`status.enabledHelp`:`status.pausedHelp`)})]}),(0,L.jsx)(`button`,{type:`button`,role:`switch`,"aria-checked":o,"aria-label":r(`status.enabledLabel`),disabled:d,onClick:D,className:`relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${o?`bg-accent-success`:`bg-input`}`,children:(0,L.jsx)(`span`,{className:`inline-block h-3.5 w-3.5 rounded-full bg-background shadow-sm transition-transform ${o?`translate-x-4`:`translate-x-0.5`}`})})]}),(0,L.jsxs)(`div`,{className:`rounded-md border border-border p-3`,"data-testid":`jira-status-map`,children:[(0,L.jsx)(`p`,{className:`text-sm font-medium`,children:r(`wizard.step3Title`)}),(0,L.jsx)(`p`,{className:`mt-0.5 mb-2 text-xs text-muted-foreground`,children:r(`mapping.intro`)}),(0,L.jsx)(`div`,{className:`space-y-2`,children:H.map(e=>(0,L.jsxs)(`label`,{className:`flex items-center justify-between gap-3`,children:[(0,L.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:r(`mapping.${U[e]}`)}),(0,L.jsxs)(`select`,{value:b[e]??``,onChange:t=>void E(e,t.target.value),className:`w-44 rounded-md border border-border bg-background px-2 py-1.5 text-sm`,"data-testid":`jira-statusmap-${e}`,children:[(0,L.jsx)(`option`,{value:``,children:r(`mapping.auto`)}),h.map(e=>(0,L.jsx)(`option`,{value:e.name,children:e.name},e.id)),b[e]&&!h.some(t=>t.name===b[e])&&(0,L.jsx)(`option`,{value:b[e],children:b[e]})]})]},e))})]}),(0,L.jsxs)(`div`,{className:`rounded-md border border-border p-3`,children:[(0,L.jsx)(`p`,{className:`text-sm font-medium`,children:r(`discard.configLabel`)}),(0,L.jsx)(`p`,{className:`mt-0.5 mb-2 text-xs text-muted-foreground`,children:r(`discard.configHelp`)}),(0,L.jsxs)(`select`,{value:_,onChange:e=>void w(e.target.value),className:`w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm`,"data-testid":`jira-discard-status-select`,children:[(0,L.jsx)(`option`,{value:``,children:r(`discard.configNone`)}),h.map(e=>(0,L.jsx)(`option`,{value:e.name,children:e.name},e.id)),_&&!h.some(e=>e.name===_)&&(0,L.jsx)(`option`,{value:_,children:_})]})]}),(0,L.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,L.jsx)(T,{variant:`outline`,size:`sm`,onClick:O,disabled:c,children:r(c?`status.syncing`:`status.syncNow`)}),(0,L.jsx)(T,{variant:`ghost`,size:`sm`,onClick:k,disabled:d,className:`text-destructive`,children:r(`status.disconnect`)})]}),(0,L.jsxs)(`div`,{className:`rounded-md border border-border p-3`,"data-testid":`jira-outbox`,children:[(0,L.jsx)(`p`,{className:`text-sm font-medium`,children:r(`outbox.title`)}),S.dead>0?(0,L.jsx)(`p`,{className:`mt-1 text-xs text-accent-warning`,children:r(`outbox.dead`,{count:S.dead})}):j>0?(0,L.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:r(`outbox.pending`,{count:j})}):(0,L.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:r(`outbox.allSynced`)}),p.length>0&&(0,L.jsxs)(`div`,{className:`mt-2 space-y-2`,children:[(0,L.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:r(`outbox.deadHelp`)}),p.map(e=>(0,L.jsxs)(`div`,{className:`flex items-center justify-between gap-2 rounded border border-border/60 px-2 py-1`,children:[(0,L.jsxs)(`span`,{className:`truncate text-xs`,children:[oe(e,r),` · `,e.deadReason??e.lastError??``]}),(0,L.jsx)(T,{variant:`outline`,size:`sm`,onClick:()=>A(e.id),children:r(`outbox.retry`)})]},e.id))]})]})]})}function oe(e,t){return e.opType===`transition`?t(`outbox.opTransition`):e.opType===`comment`?t(`outbox.opComment`):t(`outbox.opCreate`)}function G(e,t){return e instanceof Error?e.message:t(`errors.generic`)}var se=new Set([`claude`,`codex`,`kimi`]);function K(e){return{provider:e,label:e===`codex`?`Codex`:`Claude`,active:!1,available:!1,detectedRoute:!1,requests:0,inputTokens:0,inputTokensSaved:0,outputTokens:0,outputTokensSaved:0,outputSavingsPercent:0,outputSavingsMethod:`none`,outputSavingsAllocated:!1}}function q(){return{updatedAt:null,proxyStatsAvailable:!1,durableSavingsAvailable:!1,outputSavingsAvailable:!1,outputSavingsMethod:null,outputConfidence:null,providers:{codex:K(`codex`),claude:K(`claude`)},lastIssue:null}}var J={installed:!1,installSource:null,version:null,executablePath:null,uvPath:null,port:8787,phase:`idle`,activeProviders:{codex:!1,claude:!1},availableProviders:{codex:!1,claude:!1},detectedRoutes:{codex:!1,claude:!1},proxyRunning:!1,proxyPid:null,learning:{enabled:!1,baselineReady:!1,baselineSamples:0,updatedAt:null,lastIssue:null},metrics:q(),lastIssue:null,updatedAt:null};function ce(e){let t=e?.metrics??q();return{...J,...e,activeProviders:{...J.activeProviders,...e?.activeProviders},availableProviders:{...J.availableProviders,...e?.availableProviders},detectedRoutes:{...J.detectedRoutes,...e?.detectedRoutes},learning:{...J.learning,...e?.learning},metrics:{...q(),...t,providers:{codex:{...K(`codex`),...t.providers?.codex},claude:{...K(`claude`),...t.providers?.claude}}}}}async function Y(e){let t=await e.text(),n=t?JSON.parse(t):{};if(!e.ok){let t=n.issue;throw Object.assign(Error(t?.title??n.error??`HTTP ${e.status}`),{payload:n})}return n}function X(e){return`${r}/api/projects/${e}`}function Z(e,t=`en`){if(!Number.isFinite(e))return`0`;try{return Intl.NumberFormat(t,{notation:`compact`,maximumFractionDigits:1}).format(Math.max(0,e))}catch{return Intl.NumberFormat(`en`,{notation:`compact`,maximumFractionDigits:1}).format(Math.max(0,e))}}function Q(e,t,n){return`${Z(e,t)} ${n}`}function le({onWindowOpened:e}={}){let{t}=a(`integrations`),{projects:n}=c(),{registerHandler:o,unregisterHandler:s}=i(),[u,d]=(0,I.useState)(``),[p,h]=(0,I.useState)(`all`),[g,_]=(0,I.useState)(J),[v,T]=(0,I.useState)(!0),[D,k]=(0,I.useState)(null),[M,N]=(0,I.useState)([]),[re,F]=(0,I.useState)(!1),[ie,ae]=(0,I.useState)(!1),[R,z]=(0,I.useState)(null),[B,V]=(0,I.useState)({jira:0,serena:0,projects:0}),H=(0,I.useCallback)(async()=>{try{let e=await fetch(`${r}/api/global-plugins/headroom`).then(e=>Y(e));_(ce(e.state))}finally{T(!1)}},[]);(0,I.useEffect)(()=>{H()},[H]),(0,I.useEffect)(()=>(o(`plugins-page`,e=>{let t=e;t.type===`plugin.headroom_progress`&&(t.line&&N(e=>[...e.slice(-80),t.line]),H())}),()=>s(`plugins-page`)),[o,s,H]),(0,I.useEffect)(()=>{let e=!1;async function t(){let t=await Promise.all(n.map(async e=>{let[t,n]=await Promise.all([fetch(`${X(e.id)}/jira/connection`).then(e=>Y(e)).catch(()=>({connected:!1})),fetch(`${X(e.id)}/plugins`).then(e=>Y(e)).catch(()=>({plugins:[]}))]),r=n.plugins.find(e=>e.name===`serena`);return{jira:+!!t.connected,serena:r&&r.status!==`not-installed`&&r.status!==`orphan`?1:0}}));e||V({jira:t.reduce((e,t)=>e+t.jira,0),serena:t.reduce((e,t)=>e+t.serena,0),projects:n.length})}return t(),()=>{e=!0}},[n]);async function U(e,t){k(e),N([]);try{let e=(await t()).state;e&&_(ce(e))}catch(e){let t=e.payload;t?.state&&_(ce(t.state))}finally{k(null),H()}}let W=(0,I.useMemo)(()=>[{id:`headroom-ai`,title:`Headroom AI`,scope:`global`,icon:E,description:t(`plugins.catalog.headroom.description`),installed:g.installed,status:g.lastIssue?`attention`:g.installed?g.activeProviders.codex||g.activeProviders.claude?g.installSource===`system`?`external-active`:`active`:g.installSource===`system`?`external`:`installed`:`available`,statusLabel:g.lastIssue?t(`plugins.status.attention`):g.installed?g.activeProviders.codex||g.activeProviders.claude?g.installSource===`system`?t(`plugins.status.externalActive`):t(`plugins.status.active`):g.installSource===`system`?t(`plugins.status.external`):t(`plugins.status.installed`):t(`plugins.status.available`),actionKind:g.lastIssue?`review`:g.installed?`manage`:`install`,actionLabel:g.lastIssue?t(`plugins.actions.review`):g.installed?t(`plugins.actions.manage`):t(`plugins.actions.install`),busy:D===`install`,action:()=>{if(g.lastIssue||g.installed){F(!0);return}U(`install`,()=>fetch(`${r}/api/global-plugins/headroom/install`,{method:`POST`}).then(e=>Y(e)))}}],[g,D,t]),oe=(0,I.useMemo)(()=>[{id:`jira`,title:`Jira`,scope:`project`,icon:P,description:t(`plugins.catalog.jira.description`),installed:B.jira>0,status:B.jira>0?`configured`:`available`,statusLabel:B.jira>0?t(`plugins.status.configuredCount`,{count:B.jira}):t(`plugins.status.available`),actionKind:B.jira>0?`manage`:`install`,actionLabel:B.jira>0?t(`plugins.actions.manage`):t(`plugins.actions.install`),action:()=>z(`jira`)},{id:`serena`,title:`Serena`,scope:`project`,icon:te,description:t(`plugins.catalog.serena.description`),installed:B.serena>0,status:B.serena>0?`installed`:`available`,statusLabel:B.serena>0?t(`plugins.status.installedCount`,{count:B.serena}):t(`plugins.status.available`),actionKind:B.serena>0?`manage`:`install`,actionLabel:B.serena>0?t(`plugins.actions.manage`):t(`plugins.actions.install`),action:()=>z(`serena`)}],[B,t]),G=u.trim().toLowerCase(),se=e=>`${e.title} ${e.description} ${e.statusLabel} ${t(`plugins.scope.${e.scope}`)}`.toLowerCase().includes(G),K=[...W,...oe].filter(e=>p!==`all`&&e.scope!==p?!1:se(e)),q=K.filter(e=>e.scope===`global`&&e.installed),Z=K.filter(e=>e.scope===`project`&&e.installed),Q=K.filter(e=>e.scope===`global`&&!e.installed),le=K.filter(e=>e.scope===`project`&&!e.installed),fe=q.length>0||Z.length>0,pe=Q.length>0||le.length>0,me=[g.installed,B.jira>0,B.serena>0].filter(Boolean).length;return(0,L.jsxs)(`div`,{className:`flex h-full flex-col bg-background`,children:[(0,L.jsxs)(`div`,{className:`border-b border-border px-6 py-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,L.jsx)(`div`,{className:`grid h-9 w-9 place-items-center rounded-lg border border-accent-primary/25 bg-accent-primary/10 text-accent-primary`,children:(0,L.jsx)(O,{className:`h-4 w-4`})}),(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsx)(`h1`,{className:`text-lg font-semibold tracking-normal`,children:t(`plugins.page.title`)}),(0,L.jsx)(`p`,{className:`mt-0.5 text-xs text-muted-foreground`,children:t(`plugins.page.subtitle`)})]}),!A()&&(0,L.jsxs)(`button`,{type:`button`,onClick:()=>void ee().then(()=>e?.()).catch(e=>l.error(String(e))),className:`inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-2 text-xs hover:bg-muted`,children:[(0,L.jsx)(f,{className:`h-3.5 w-3.5`}),t(`plugins.page.openWindow`)]}),(0,L.jsxs)(`div`,{className:`mr-12 flex items-center gap-2 rounded-lg border border-border bg-card/60 px-3 py-2 text-xs`,children:[(0,L.jsx)(j,{className:`h-3.5 w-3.5 text-accent-success`}),(0,L.jsx)(`span`,{className:`text-muted-foreground`,children:t(`plugins.page.readyCount`,{count:me})})]})]}),(0,L.jsxs)(`div`,{className:`mt-4 flex flex-col gap-3 md:flex-row md:items-center`,children:[(0,L.jsxs)(`label`,{className:`relative flex-1`,children:[(0,L.jsx)(m,{className:`pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground`}),(0,L.jsx)(`input`,{value:u,onChange:e=>d(e.target.value),placeholder:t(`plugins.search.placeholder`),className:`h-9 w-full rounded-lg border border-border bg-background pl-9 pr-3 text-sm outline-none transition-colors focus:border-accent-primary/60`})]}),(0,L.jsx)(`div`,{className:`inline-flex h-9 rounded-lg border border-border bg-card p-1`,children:[`all`,`global`,`project`].map(e=>(0,L.jsx)(`button`,{type:`button`,onClick:()=>h(e),className:w(`rounded-md px-3 text-xs capitalize transition-colors`,p===e?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`),children:t(`plugins.filters.${e}`)},e))})]})]}),(0,L.jsxs)(`div`,{className:`flex-1 overflow-auto px-6 py-5`,children:[(0,L.jsxs)(`section`,{className:`mb-6`,children:[(0,L.jsxs)(`div`,{className:`mb-3 flex items-center gap-2 text-xs font-medium text-muted-foreground`,children:[(0,L.jsx)(ne,{className:`h-3.5 w-3.5`}),t(`plugins.sections.installed`)]}),fe?(0,L.jsx)(ue,{globalPlugins:q,projectPlugins:Z}):(0,L.jsx)(de,{text:t(G?`plugins.empty.installedSearch`:`plugins.empty.installed`)})]}),(0,L.jsxs)(`section`,{children:[(0,L.jsxs)(`div`,{className:`mb-3 flex items-center gap-2 text-xs font-medium text-muted-foreground`,children:[(0,L.jsx)(O,{className:`h-3.5 w-3.5`}),t(`plugins.sections.available`)]}),pe?(0,L.jsx)(ue,{globalPlugins:Q,projectPlugins:le}):(0,L.jsx)(de,{text:t(G?`plugins.empty.availableSearch`:`plugins.empty.allInstalled`)})]})]}),(0,L.jsx)(C,{open:re,onOpenChange:F,children:(0,L.jsxs)(S,{className:`w-[calc(100vw-2rem)] max-w-xl overflow-x-hidden p-4 sm:p-6`,children:[(0,L.jsxs)(x,{children:[(0,L.jsx)(b,{children:t(`plugins.headroom.title`)}),(0,L.jsx)(y,{children:t(`plugins.headroom.dialogDescription`)})]}),(0,L.jsx)(he,{state:g,loading:v,busy:D,logs:M,onInstall:()=>U(`install`,()=>fetch(`${r}/api/global-plugins/headroom/install`,{method:`POST`}).then(e=>Y(e))),onToggleProvider:(e,t)=>U(`${t?`deactivate`:`activate`}-${e}`,()=>fetch(`${r}/api/global-plugins/headroom/${t?`deactivate`:`activate`}`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({provider:e})}).then(e=>Y(e))),onDeactivateAll:()=>U(`deactivate-all`,async()=>{let e={state:g};for(let t of[`codex`,`claude`])g.activeProviders[t]&&(e=await fetch(`${r}/api/global-plugins/headroom/deactivate`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({provider:t})}).then(e=>Y(e)));return e}),onUninstall:()=>U(`uninstall`,()=>fetch(`${r}/api/global-plugins/headroom/uninstall`,{method:`POST`}).then(e=>Y(e))),onSetPort:e=>U(`port`,()=>fetch(`${r}/api/global-plugins/headroom/port`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({port:e})}).then(e=>Y(e))),onOpenDiagnostics:()=>ae(!0)})]})}),(0,L.jsx)(xe,{plugin:R,projects:n,onClose:()=>z(null)}),(0,L.jsx)(we,{open:ie,onClose:()=>ae(!1)})]})}function ue({globalPlugins:e,projectPlugins:t}){let{t:n}=a(`integrations`);return(0,L.jsxs)(`div`,{className:w(`grid grid-cols-1 gap-4`,e.length>0&&t.length>0&&`xl:grid-cols-2`),children:[e.length>0&&(0,L.jsxs)(`section`,{children:[(0,L.jsx)(pe,{title:n(`plugins.groups.global.title`),detail:n(`plugins.groups.global.detail`),scope:`global`}),(0,L.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fill,minmax(260px,320px))] justify-start gap-3`,children:e.map(e=>(0,L.jsx)(me,{plugin:e},e.id))})]}),t.length>0&&(0,L.jsxs)(`section`,{children:[(0,L.jsx)(pe,{title:n(`plugins.groups.project.title`),detail:n(`plugins.groups.project.detail`),scope:`project`}),(0,L.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fill,minmax(260px,320px))] justify-start gap-3`,children:t.map(e=>(0,L.jsx)(me,{plugin:e},e.id))})]})]})}function de({text:e}){return(0,L.jsx)(`div`,{className:`flex h-[74px] max-w-[320px] items-center rounded-lg border border-dashed border-border bg-card/35 px-3 text-xs text-muted-foreground`,children:e})}function fe({scope:e}){let{t}=a(`integrations`);return(0,L.jsx)(`span`,{className:w(`rounded px-1.5 py-0.5 text-[10px] uppercase tracking-wide`,e===`global`?`bg-accent-primary/10 text-accent-primary`:`bg-accent-info/10 text-accent-info`),children:t(`plugins.scope.${e}`)})}function pe({title:e,detail:t,scope:n}){return(0,L.jsxs)(`div`,{className:`mb-2 flex items-center gap-2`,children:[(0,L.jsx)(`div`,{className:w(`h-px flex-1`,n===`global`?`bg-accent-primary/30`:`bg-accent-info/30`)}),(0,L.jsxs)(`div`,{className:`flex items-center gap-2 rounded-md border border-border bg-card/70 px-2 py-1`,children:[(0,L.jsx)(fe,{scope:n}),(0,L.jsx)(`span`,{className:`text-xs font-medium`,children:e}),(0,L.jsx)(`span`,{className:`hidden text-[11px] text-muted-foreground sm:inline`,children:t})]}),(0,L.jsx)(`div`,{className:w(`h-px flex-1`,n===`global`?`bg-accent-primary/30`:`bg-accent-info/30`)})]})}function me({plugin:e}){let{t}=a(`integrations`),n=e.icon,r=e.scope===`global`,i=e.status===`attention`,o=e.busy?p:e.actionKind===`install`?d:e.actionKind===`review`?g:F,s=w(`shrink-0 rounded border px-1.5 py-0.5 text-[10px] leading-none`,i?`border-accent-warning/30 bg-accent-warning/10 text-accent-warning`:e.installed?`border-accent-success/25 bg-accent-success/10 text-accent-success`:`border-border bg-muted text-muted-foreground`);return(0,L.jsxs)(`article`,{className:`grid h-[88px] w-full grid-cols-[38px_minmax(0,1fr)_auto] items-center gap-3 rounded-lg border border-border bg-card/70 px-3 transition-colors hover:border-accent-primary/35 hover:bg-card`,children:[(0,L.jsx)(`div`,{className:w(`grid h-[38px] w-[38px] shrink-0 place-items-center rounded-lg border`,r?`border-accent-primary/25 bg-accent-primary/10 text-accent-primary`:`border-accent-info/25 bg-accent-info/10 text-accent-info`),children:(0,L.jsx)(n,{className:`h-4 w-4`})}),(0,L.jsxs)(`div`,{className:`min-w-0`,children:[(0,L.jsx)(`h2`,{className:`truncate text-sm font-semibold leading-5`,children:e.title}),(0,L.jsx)(`p`,{className:`mt-1 truncate text-[11px] leading-4 text-muted-foreground`,children:e.description}),(0,L.jsxs)(`div`,{className:`mt-1.5 flex items-center gap-1.5`,children:[(0,L.jsx)(fe,{scope:e.scope}),(0,L.jsx)(`span`,{className:s,children:e.statusLabel})]})]}),(0,L.jsxs)(`button`,{type:`button`,disabled:e.busy,onClick:e.action,className:`flex h-7 shrink-0 items-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-xs font-medium transition-colors hover:bg-muted disabled:opacity-50`,children:[(0,L.jsx)(o,{className:w(`h-3.5 w-3.5`,e.busy&&`animate-spin`)}),e.busy?t(`plugins.actions.installing`):e.actionLabel]})]})}function he({state:e,loading:t,busy:n,logs:r,onInstall:i,onToggleProvider:o,onDeactivateAll:s,onUninstall:c,onSetPort:l,onOpenDiagnostics:u}){let{t:f}=a(`integrations`),[m,g]=(0,I.useState)(String(e.port)),[v,y]=(0,I.useState)(!1);(0,I.useEffect)(()=>g(String(e.port)),[e.port]),(0,I.useEffect)(()=>{e.installed||y(!1)},[e.installed]);let b=e.activeProviders.codex||e.activeProviders.claude,x=e.installSource===`system`;return(0,L.jsxs)(`aside`,{className:`w-full min-w-0 overflow-hidden rounded-lg border border-border bg-card/80 p-4`,children:[(0,L.jsxs)(`div`,{className:`mb-4 flex items-start gap-3`,children:[(0,L.jsx)(`div`,{className:`grid h-10 w-10 place-items-center rounded-lg border border-accent-primary/25 bg-accent-primary/10 text-accent-primary`,children:(0,L.jsx)(E,{className:`h-4 w-4`})}),(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsx)(`h2`,{className:`text-sm font-semibold`,children:f(`plugins.headroom.title`)}),(0,L.jsx)(`p`,{className:`mt-1 text-xs leading-relaxed text-muted-foreground`,children:f(`plugins.headroom.panelDescription`)})]})]}),(0,L.jsxs)(`div`,{className:`mb-4 grid grid-cols-3 gap-2 text-xs`,children:[(0,L.jsx)(ge,{label:f(`plugins.headroom.metrics.install`),value:t?f(`plugins.headroom.metrics.checking`):e.installed?f(`plugins.headroom.metrics.ready`):f(`plugins.headroom.metrics.missing`),good:e.installed}),(0,L.jsx)(ge,{label:f(`plugins.headroom.metrics.source`),value:e.installSource===`system`?f(`plugins.headroom.metrics.system`):e.installSource===`managed`?f(`plugins.headroom.metrics.specrails`):f(`plugins.headroom.metrics.none`),good:e.installed}),(0,L.jsx)(ge,{label:f(`plugins.headroom.metrics.proxy`),value:e.proxyRunning?`:${e.port}`:f(b?`plugins.headroom.metrics.starting`:`plugins.headroom.metrics.off`),good:e.proxyRunning||!b})]}),e.lastIssue&&(0,L.jsx)(_e,{issue:e.lastIssue}),e.installed?(0,L.jsxs)(`div`,{className:`mb-4 space-y-2`,children:[(0,L.jsx)(be,{provider:`codex`,active:e.activeProviders.codex,available:e.availableProviders.codex,detectedRoute:e.detectedRoutes.codex,busy:n===`activate-codex`||n===`deactivate-codex`,onClick:()=>o(`codex`,e.activeProviders.codex)}),(0,L.jsx)(be,{provider:`claude`,active:e.activeProviders.claude,available:e.availableProviders.claude,detectedRoute:e.detectedRoutes.claude,busy:n===`activate-claude`||n===`deactivate-claude`,onClick:()=>o(`claude`,e.activeProviders.claude)}),b&&(0,L.jsxs)(`button`,{type:`button`,disabled:!!n,onClick:s,className:`flex h-8 w-full items-center justify-center gap-2 rounded-lg border border-border bg-background/45 text-xs hover:bg-muted/50 disabled:opacity-50`,children:[n===`deactivate-all`?(0,L.jsx)(p,{className:`h-3.5 w-3.5 animate-spin`}):(0,L.jsx)(re,{className:`h-3.5 w-3.5`}),f(`plugins.headroom.actions.disableAllRoutes`)]})]}):(0,L.jsxs)(`button`,{type:`button`,disabled:!!n,onClick:i,className:`mb-3 flex h-9 w-full items-center justify-center gap-2 rounded-lg bg-accent-primary px-3 text-xs font-medium text-white disabled:opacity-50`,children:[n===`install`?(0,L.jsx)(p,{className:`h-3.5 w-3.5 animate-spin`}):(0,L.jsx)(d,{className:`h-3.5 w-3.5`}),f(`plugins.headroom.actions.installWithUv`)]}),e.installed&&(0,L.jsx)(ve,{state:e}),(0,L.jsxs)(`div`,{className:`mb-4 rounded-lg border border-border bg-background/45 p-3`,children:[(0,L.jsx)(`label`,{className:`mb-1 block text-[11px] font-medium text-muted-foreground`,children:f(`plugins.headroom.port.label`)}),(0,L.jsxs)(`div`,{className:`flex gap-2`,children:[(0,L.jsx)(`input`,{value:m,onChange:e=>g(e.target.value),className:`h-8 min-w-0 flex-1 rounded-md border border-border bg-background px-2 text-sm`}),(0,L.jsx)(`button`,{type:`button`,disabled:!!n||Number(m)===e.port,onClick:()=>l(Number(m)),className:`rounded-md border border-border px-2 text-xs hover:bg-muted disabled:opacity-50`,children:f(`plugins.headroom.port.apply`)})]})]}),r.length>0&&(0,L.jsx)(`pre`,{className:`mb-3 max-h-32 overflow-y-auto overflow-x-hidden whitespace-pre-wrap break-words rounded-lg border border-border bg-background/60 p-2 text-[10px] text-muted-foreground`,children:r.join(`
2
- `)}),(0,L.jsxs)(`button`,{type:`button`,onClick:u,className:`mb-2 flex h-8 w-full items-center justify-center gap-2 rounded-lg border border-border text-xs hover:bg-muted/50`,children:[(0,L.jsx)(_,{className:`h-3.5 w-3.5`}),f(`plugins.headroom.actions.viewDiagnostics`)]}),e.installed&&(0,L.jsx)(`div`,{className:`rounded-lg border border-destructive/25 bg-destructive/5 p-3`,children:v?(0,L.jsxs)(`div`,{className:`space-y-2`,children:[(0,L.jsx)(`p`,{className:`text-[11px] leading-relaxed text-muted-foreground`,children:f(x?`plugins.headroom.confirmDetach`:`plugins.headroom.confirmUninstall`)}),(0,L.jsxs)(`div`,{className:`grid grid-cols-2 gap-2`,children:[(0,L.jsx)(`button`,{type:`button`,disabled:!!n,onClick:()=>y(!1),className:`h-8 rounded-md border border-border text-xs hover:bg-muted/50 disabled:opacity-50`,children:f(`plugins.actions.cancel`)}),(0,L.jsxs)(`button`,{type:`button`,disabled:!!n,onClick:c,className:`flex h-8 items-center justify-center gap-2 rounded-md bg-destructive px-2 text-xs font-medium text-destructive-foreground disabled:opacity-50`,children:[n===`uninstall`?(0,L.jsx)(p,{className:`h-3.5 w-3.5 animate-spin`}):(0,L.jsx)(h,{className:`h-3.5 w-3.5`}),f(x?`plugins.headroom.actions.detach`:`plugins.headroom.actions.uninstall`)]})]})]}):(0,L.jsxs)(`button`,{type:`button`,disabled:!!n,onClick:()=>y(!0),className:`flex h-8 w-full items-center justify-center gap-2 rounded-md text-xs font-medium text-destructive hover:bg-destructive/10 disabled:opacity-50`,children:[(0,L.jsx)(h,{className:`h-3.5 w-3.5`}),f(x?`plugins.headroom.actions.detachHeadroom`:`plugins.headroom.actions.uninstallHeadroom`)]})})]})}function ge({label:e,value:t,good:n}){return(0,L.jsxs)(`div`,{className:`rounded-lg border border-border bg-background/45 p-2`,children:[(0,L.jsx)(`div`,{className:`text-[10px] text-muted-foreground`,children:e}),(0,L.jsx)(`div`,{className:w(`mt-0.5 font-medium`,n?`text-foreground`:`text-accent-warning`),children:t})]})}function _e({issue:e}){let{t}=a(`integrations`),n=`plugins.headroom.issues.${e.code}`,r=t(`${n}.title`,{defaultValue:e.title}),i=t(`${n}.guidance`,{defaultValue:e.guidance});return(0,L.jsx)(`div`,{className:`mb-3 rounded-lg border border-accent-warning/35 bg-accent-warning/10 p-3`,children:(0,L.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,L.jsx)(g,{className:`mt-0.5 h-3.5 w-3.5 shrink-0 text-accent-warning`}),(0,L.jsxs)(`div`,{className:`min-w-0`,children:[(0,L.jsx)(`div`,{className:`text-xs font-medium`,children:r}),(0,L.jsx)(`p`,{className:`mt-1 text-[11px] leading-relaxed text-muted-foreground`,children:i}),(0,L.jsx)(`div`,{className:`mt-1 text-[10px] text-muted-foreground`,children:t(`plugins.headroom.issueCode`,{code:e.code})})]})]})})}function ve({state:e}){let{t,i18n:n}=a(`integrations`),r=e.metrics,i=r.outputSavingsAvailable,o=[`codex`,`claude`].map(e=>({provider:e,metric:r.providers[e]})),s=t(`plugins.headroom.savings.tokens`),c=o.reduce((e,{metric:t})=>({inputTokens:e.inputTokens+t.inputTokens,inputTokensSaved:e.inputTokensSaved+t.inputTokensSaved,outputTokens:e.outputTokens+t.outputTokens,outputTokensSaved:e.outputTokensSaved+t.outputTokensSaved}),{inputTokens:0,inputTokensSaved:0,outputTokens:0,outputTokensSaved:0}),l=o.some(({metric:e})=>e.inputTokensSaved>0),u=o.some(({metric:e})=>e.active),d=t(i?`plugins.headroom.savings.statusInputOutput`:l?`plugins.headroom.savings.statusInputOnly`:u?`plugins.headroom.savings.statusRouting`:`plugins.headroom.savings.statusInactive`),f=i||l;return(0,L.jsxs)(`div`,{className:`mb-4 rounded-lg border border-border bg-background/45 p-3`,children:[(0,L.jsxs)(`div`,{className:`mb-2 flex items-center justify-between gap-2`,children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,L.jsx)(M,{className:`h-3.5 w-3.5 text-accent-primary`}),(0,L.jsx)(`div`,{className:`truncate text-xs font-medium`,children:t(`plugins.headroom.savings.title`)})]}),(0,L.jsx)(`span`,{className:w(`shrink-0 rounded border px-1.5 py-0.5 text-[10px]`,f?`border-accent-success/25 bg-accent-success/10 text-accent-success`:u?`border-accent-primary/25 bg-accent-primary/10 text-accent-primary`:`border-border bg-muted text-muted-foreground`),children:d})]}),(0,L.jsxs)(`div`,{className:`mb-2 rounded-md bg-muted/35 px-2.5 py-2`,children:[(0,L.jsx)(`div`,{className:`mb-1.5 text-[10px] font-medium uppercase text-muted-foreground`,children:t(`plugins.headroom.savings.totals`)}),(0,L.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,L.jsx)($,{label:t(`plugins.headroom.savings.input`),processed:c.inputTokens>0?Q(c.inputTokens,n.language,s):t(`plugins.headroom.savings.noData`),saved:Q(c.inputTokensSaved,n.language,s)}),(0,L.jsx)($,{align:`right`,label:t(`plugins.headroom.savings.output`),processed:c.outputTokens>0?Q(c.outputTokens,n.language,s):t(`plugins.headroom.savings.noData`),saved:i?Q(c.outputTokensSaved,n.language,s):t(`plugins.headroom.savings.noData`)})]})]}),(0,L.jsx)(`div`,{className:`grid gap-2`,children:o.map(({provider:e,metric:t})=>(0,L.jsx)(ye,{metric:t,outputAvailable:r.outputSavingsAvailable},e))}),(0,L.jsx)(`div`,{className:`mt-2 flex items-center justify-end gap-2 text-[10px] text-muted-foreground`,children:r.updatedAt?t(`plugins.headroom.savings.updatedLive`):t(`plugins.headroom.savings.baselineSamples`,{count:e.learning.baselineSamples})})]})}function $({label:e,processed:t,saved:n,detail:r,align:i=`left`}){let{t:o}=a(`integrations`);return(0,L.jsxs)(`div`,{className:w(`min-w-0`,i===`right`&&`text-right`),children:[(0,L.jsx)(`div`,{className:`text-[10px] font-medium uppercase text-muted-foreground`,children:e}),(0,L.jsxs)(`div`,{className:`mt-1 space-y-0.5 text-[11px]`,children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 items-baseline justify-between gap-2`,children:[(0,L.jsx)(`span`,{className:`shrink-0 text-muted-foreground`,children:o(`plugins.headroom.savings.processed`)}),(0,L.jsx)(`span`,{className:`truncate font-semibold tabular-nums text-foreground`,children:t})]}),(0,L.jsxs)(`div`,{className:`flex min-w-0 items-baseline justify-between gap-2`,children:[(0,L.jsx)(`span`,{className:`shrink-0 text-muted-foreground`,children:o(`plugins.headroom.savings.saved`)}),(0,L.jsx)(`span`,{className:`truncate font-semibold tabular-nums text-foreground`,children:n})]})]}),r&&(0,L.jsx)(`div`,{className:`mt-1 truncate text-[10px] text-muted-foreground`,children:r})]})}function ye({metric:e,outputAvailable:t}){let{t:n,i18n:r}=a(`integrations`),i=!e.available,o=i||!e.active,s=n(`plugins.headroom.savings.tokens`),c=e.inputTokens>0?Q(e.inputTokens,r.language,s):n(`plugins.headroom.savings.noData`),l=Q(e.inputTokensSaved,r.language,s),u=e.outputTokens>0?Q(e.outputTokens,r.language,s):n(`plugins.headroom.savings.noData`),d=t?Q(e.outputTokensSaved,r.language,s):e.active?n(`plugins.headroom.savings.noData`):n(`plugins.headroom.savings.off`),f=e.requests>0?n(`plugins.headroom.savings.requestCount`,{count:Z(e.requests,r.language)}):i?n(`plugins.headroom.provider.noProjectUsesProvider`):e.active?n(`plugins.headroom.savings.inputSavingsActive`):n(`plugins.headroom.savings.routeDisabled`);return(0,L.jsxs)(`div`,{className:w(`rounded-md border border-border bg-card/45 px-2.5 py-2`,o&&`opacity-70`),children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 items-center gap-1.5`,children:[(0,L.jsx)(`span`,{className:`text-xs font-medium`,children:e.label}),e.detectedRoute&&(0,L.jsx)(`span`,{className:`rounded bg-accent-primary/10 px-1.5 py-0.5 text-[9px] uppercase text-accent-primary`,children:n(`plugins.headroom.savings.detected`)})]}),(0,L.jsx)(`div`,{className:`mt-0.5 truncate text-[10px] text-muted-foreground`,children:f}),(0,L.jsxs)(`div`,{className:`mt-2 grid grid-cols-2 gap-3`,children:[(0,L.jsx)($,{label:n(`plugins.headroom.savings.input`),processed:c,saved:l}),(0,L.jsx)($,{align:`right`,label:n(`plugins.headroom.savings.output`),processed:u,saved:d})]})]})}function be({provider:e,active:t,available:n,detectedRoute:r,busy:i,onClick:o}){let{t:s}=a(`integrations`),c=s(n?t?`plugins.headroom.provider.routedThroughHeadroom`:r?`plugins.headroom.provider.detectedExternalRoute`:`plugins.headroom.provider.notRouted`:`plugins.headroom.provider.noProjectUsesProvider`);return(0,L.jsxs)(`button`,{type:`button`,onClick:o,disabled:i||!n,className:`flex w-full items-center gap-3 rounded-lg border border-border bg-background/45 p-3 text-left hover:bg-muted/35 disabled:opacity-50`,children:[(0,L.jsx)(`div`,{className:w(`grid h-8 w-8 place-items-center rounded-md`,t?`bg-accent-success/15 text-accent-success`:`bg-muted text-muted-foreground`),children:i?(0,L.jsx)(p,{className:`h-3.5 w-3.5 animate-spin`}):(0,L.jsx)(N,{className:`h-3.5 w-3.5`})}),(0,L.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,L.jsx)(`div`,{className:`text-xs font-medium capitalize`,children:e}),(0,L.jsx)(`div`,{className:`text-[11px] text-muted-foreground`,children:c})]}),(0,L.jsx)(`span`,{className:w(`h-5 w-9 rounded-full p-0.5 transition-colors`,t?`bg-accent-success`:`bg-muted`),children:(0,L.jsx)(`span`,{className:w(`block h-4 w-4 rounded-full bg-white shadow transition-transform`,t&&`translate-x-4`)})})]})}function xe({plugin:e,projects:t,onClose:n}){let{t:r}=a(`integrations`),[i,o]=(0,I.useState)(``);(0,I.useEffect)(()=>o(``),[e]);let c=t.find(e=>e.id===i)??null;return(0,L.jsx)(C,{open:!!e,onOpenChange:e=>{e||n()},children:(0,L.jsxs)(S,{className:`w-[calc(100vw-2rem)] max-w-3xl overflow-x-hidden p-4 sm:p-6`,children:[(0,L.jsxs)(x,{children:[(0,L.jsx)(b,{children:r(e===`jira`?`plugins.projectWizard.configureJira`:`plugins.projectWizard.installSerena`)}),(0,L.jsx)(y,{children:r(`plugins.projectWizard.description`)})]}),(0,L.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-[260px_minmax(0,1fr)]`,children:[(0,L.jsxs)(`div`,{className:`rounded-lg border border-border bg-card/70 p-3`,children:[(0,L.jsx)(`div`,{className:`mb-2 text-xs font-medium text-muted-foreground`,children:r(`plugins.projectWizard.chooseProject`)}),(0,L.jsx)(`div`,{className:`max-h-[52vh] space-y-1 overflow-auto`,children:t.map(t=>{let n=e===`serena`&&!s(t).some(e=>se.has(e));return(0,L.jsxs)(`button`,{type:`button`,disabled:n,onClick:()=>o(t.id),className:w(`w-full rounded-md px-2 py-2 text-left transition-colors`,t.id===i?`bg-muted text-foreground`:`text-muted-foreground hover:bg-muted/45 hover:text-foreground`,n&&`cursor-not-allowed opacity-45`),children:[(0,L.jsx)(`div`,{className:`truncate text-xs font-medium`,children:t.name}),(0,L.jsx)(`div`,{className:`truncate text-[10px]`,children:n?r(`plugins.projectWizard.requiresSupportedProvider`,{defaultValue:`Requires Claude or Codex`}):t.path})]},t.id)})})]}),(0,L.jsx)(`div`,{className:`min-h-[360px] rounded-lg border border-border bg-card/70 p-4`,children:c?e===`jira`?(0,L.jsx)(Se,{project:c,onDone:n}):(0,L.jsx)(Ce,{project:c,onDone:n}):(0,L.jsx)(`div`,{className:`flex h-full items-center justify-center text-sm text-muted-foreground`,children:r(`plugins.projectWizard.selectProjectToContinue`)})})]})]})})}function Se({project:e,onDone:t}){let{t:n}=a(`integrations`),r=X(e.id),[i,o]=(0,I.useState)(null),[s,c]=(0,I.useState)(!0),[l,u]=(0,I.useState)(null),d=(0,I.useCallback)(async()=>{c(!0),u(null);try{let e=await fetch(`${r}/jira/connection`).then(e=>Y(e));o(e)}catch(e){o({connected:!1}),u(e.message)}finally{c(!1)}},[r]);return(0,I.useEffect)(()=>{d()},[d]),s?(0,L.jsxs)(`div`,{className:`flex h-full items-center justify-center text-sm text-muted-foreground`,children:[(0,L.jsx)(p,{className:`mr-2 h-4 w-4 animate-spin`}),n(`plugins.jira.loading`,{defaultValue:`Loading Jira...`})]}):i?.connected&&i.connection?(0,L.jsxs)(`div`,{className:`space-y-4`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`h3`,{className:`text-sm font-semibold`,children:n(`plugins.jira.titleForProject`,{name:e.name,defaultValue:`Jira for ${e.name}`})}),(0,L.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:n(`plugins.jira.configuredDescription`,{defaultValue:`Manage the Jira project, status mapping, discard status, sync, and connection state.`})})]}),(0,L.jsx)(W,{state:i,apiBase:r,onChanged:()=>{d()}})]}):(0,L.jsxs)(`div`,{className:`space-y-4`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`h3`,{className:`text-sm font-semibold`,children:n(`plugins.jira.titleForProject`,{name:e.name,defaultValue:`Jira for ${e.name}`})}),(0,L.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:l?n(`plugins.jira.loadFailed`,{message:l,defaultValue:`Could not load the current Jira configuration: ${l}`}):n(`plugins.jira.notConfiguredDescription`,{defaultValue:`Connect this project to Jira.`})})]}),(0,L.jsx)(R,{apiBase:r,onConnected:()=>{d()},onSkip:t})]})}function Ce({project:e,onDone:t}){let{t:n}=a(`integrations`),r=(0,I.useMemo)(()=>s(e).filter(e=>se.has(e)),[e]),i=r.includes(e.provider)?e.provider:r[0]??e.provider,[o,c]=(0,I.useState)(i),[l,f]=(0,I.useState)(null),[m,h]=(0,I.useState)(null),[_,v]=(0,I.useState)(!1),[y,b]=(0,I.useState)(null),x=(0,I.useRef)(0);(0,I.useEffect)(()=>{c(i),f(null),h(null)},[e.id,i]);let S=(0,I.useCallback)(async()=>{let t=++x.current;b(null);try{let n=`?provider=${encodeURIComponent(o)}`,r=await fetch(`${X(e.id)}/plugins${n}`).then(e=>Y(e));if(t!==x.current)return;let i=r.plugins.find(e=>e.name===`serena`)??null;if(f(i),h(null),i?.status===`not-installed`){let r=await fetch(`${X(e.id)}/plugins/serena/preview-install${n}`).then(e=>Y(e));t===x.current&&h(r)}}catch(e){t===x.current&&b(e.message)}},[e.id,o]);(0,I.useEffect)(()=>{S()},[S]);async function C(){v(!0),b(null);try{await fetch(`${X(e.id)}/plugins/serena/install`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({provider:o})}).then(e=>Y(e)),await S(),t()}catch(e){b(e.message)}finally{v(!1)}}let w=(m?.requirements??[]).every(e=>e.installed&&e.executable&&e.meetsMinimum),T=l?n(`plugins.projectStatus.${l.status}`,{defaultValue:l.status}):``,E=l?.name===`serena`?n(`plugins.catalog.serena.description`):l?.description;return!l&&!y?(0,L.jsxs)(`div`,{className:`flex h-full items-center justify-center text-sm text-muted-foreground`,children:[(0,L.jsx)(p,{className:`mr-2 h-4 w-4 animate-spin`}),` `,n(`plugins.serena.loading`)]}):(0,L.jsxs)(`div`,{className:`space-y-4`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`h3`,{className:`text-sm font-semibold`,children:n(`plugins.serena.titleForProject`,{name:e.name})}),(0,L.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:n(`plugins.serena.description`)})]}),(0,L.jsxs)(`label`,{className:`block space-y-1`,children:[(0,L.jsx)(`span`,{className:`text-xs font-medium`,children:n(`plugins.serena.provider`,{defaultValue:`AI provider`})}),(0,L.jsx)(`select`,{"aria-label":n(`plugins.serena.provider`,{defaultValue:`AI provider`}),value:o,disabled:_,onChange:e=>{f(null),h(null),c(e.target.value)},className:`w-full rounded-md border border-border bg-background px-2 py-1.5 text-xs`,children:r.map(e=>(0,L.jsx)(`option`,{value:e,children:u(e)},e))})]}),l&&(0,L.jsxs)(`div`,{className:`rounded-lg border border-border bg-background/45 p-3`,children:[(0,L.jsx)(`div`,{className:`text-xs font-medium`,children:n(`plugins.serena.status`,{status:T})}),E&&(0,L.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:E})]}),m&&(0,L.jsxs)(`div`,{className:`space-y-3`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`div`,{className:`mb-1 text-xs font-medium`,children:n(`plugins.serena.files`)}),(0,L.jsx)(`ul`,{className:`space-y-1 text-xs font-mono`,children:m.files.map(e=>(0,L.jsxs)(`li`,{className:e.op===`create`?`text-accent-success`:`text-accent-info`,children:[e.op===`create`?`+ `:`~ `,e.path]},e.path))})]}),(0,L.jsxs)(`div`,{children:[(0,L.jsx)(`div`,{className:`mb-1 text-xs font-medium`,children:n(`plugins.serena.prerequisites`)}),(0,L.jsx)(`ul`,{className:`space-y-1 text-xs`,children:m.requirements.map(e=>(0,L.jsxs)(`li`,{className:`flex items-center gap-2`,children:[e.installed&&e.executable&&e.meetsMinimum?(0,L.jsx)(D,{className:`h-3 w-3 text-accent-success`}):(0,L.jsx)(g,{className:`h-3 w-3 text-accent-warning`}),(0,L.jsxs)(`span`,{children:[e.name,e.minVersion?` >= ${e.minVersion}`:``]}),e.version&&(0,L.jsxs)(`span`,{className:`text-muted-foreground`,children:[`(`,e.version,`)`]})]},e.name))})]})]}),y&&(0,L.jsx)(`div`,{className:`rounded-lg border border-destructive/30 bg-destructive/10 p-3 text-xs text-destructive`,children:y}),(0,L.jsxs)(`div`,{className:`flex justify-end gap-2`,children:[(0,L.jsx)(`button`,{type:`button`,onClick:t,className:`rounded-md border border-border px-3 py-1.5 text-xs`,children:n(`plugins.actions.cancel`)}),(0,L.jsxs)(`button`,{type:`button`,disabled:_||l?.status!==`not-installed`||!w,onClick:C,className:`flex items-center gap-2 rounded-md bg-accent-primary px-3 py-1.5 text-xs text-white disabled:opacity-50`,children:[_?(0,L.jsx)(p,{className:`h-3.5 w-3.5 animate-spin`}):(0,L.jsx)(d,{className:`h-3.5 w-3.5`}),n(`plugins.actions.install`)]})]})]})}function we({open:e,onClose:t}){let{t:n}=a(`integrations`),[i,o]=(0,I.useState)(null);return(0,I.useEffect)(()=>{e&&fetch(`${r}/api/global-plugins/headroom/diagnostics`).then(e=>Y(e)).then(o).catch(e=>o({error:e.message}))},[e]),(0,L.jsx)(C,{open:e,onOpenChange:e=>{e||t()},children:(0,L.jsxs)(S,{className:`max-w-3xl`,children:[(0,L.jsxs)(x,{children:[(0,L.jsxs)(b,{className:`flex items-center gap-2`,children:[(0,L.jsx)(F,{className:`h-4 w-4`}),` `,n(`plugins.diagnostics.title`)]}),(0,L.jsx)(y,{children:n(`plugins.diagnostics.description`)})]}),(0,L.jsx)(`pre`,{className:`max-h-[62vh] overflow-y-auto overflow-x-hidden whitespace-pre-wrap break-words rounded-lg border border-border bg-background p-3 text-xs`,children:JSON.stringify(i,null,2)})]})})}export{le as default};
@@ -1 +0,0 @@
1
- var e={title:`Jira`,subtitle:`Specs dieses Projekts mit einem Jira-Board synchronisieren`,connectCta:`Mit Jira verbinden`},t={step:`Schritt {{current}} von {{total}}`,step1Title:`Verbinde dein Jira-Konto`,step2Title:`Wähle ein Jira-Projekt`,step3Title:`Status zuordnen (optional)`,step4Title:`Prüfen und verbinden`,next:`Weiter`,back:`Zurück`,cancel:`Abbrechen`,connect:`Verbinden`,connecting:`Verbinde…`,later:`Später erledigen`},n={baseUrlLabel:`Jira-URL`,baseUrlPlaceholder:`https://dein-unternehmen.atlassian.net`,baseUrlHelp:`Die URL deiner Jira-Cloud-Site oder deines Data-Center-Servers.`,emailLabel:`Konto-E-Mail`,emailPlaceholder:`du@unternehmen.com`,emailHelp:`Für Jira Cloud erforderlich — die E-Mail deines Atlassian-Kontos.`,tokenLabel:`API-Token`,tokenHelp:`Erstelle einen unter id.atlassian.com → Sicherheit → API-Tokens. Er wird verschlüsselt auf diesem Gerät gespeichert und verlässt es nie.`,test:`Verbindung testen`,testing:`Teste…`,testOk:`Verbunden als {{name}}`,retest:`Erneut testen`},r={searchPlaceholder:`Projekte suchen…`,manualLabel:`Oder gib einen Projektschlüssel ein`,manualPlaceholder:`PROJ`,selected:`{{key}} — {{name}}`,none:`Keine Projekte gefunden`,loading:`Projekte werden geladen…`},i={intro:`Status werden automatisch nach Kategorie zugeordnet (To Do / In Arbeit / Fertig). Überschreibe einen Status, wenn dein Workflow eigene Namen verwendet.`,auto:`Automatisch (nach Kategorie)`,todo:`Backlog / To Do`,inProgress:`In Arbeit`,onReview:`In Prüfung`,done:`Fertig`,cancelled:`Abgebrochen`,loading:`Status werden geladen…`},a={site:`Site`,project:`Projekt`,account:`Konto`,type:`Typ`,cloud:`Jira Cloud`,dc:`Jira Data Center`},o={connected:`Verbunden mit {{key}}`,enabledLabel:`Synchronisierung aktiviert`,enabledHelp:`Wenn aktiviert, werden Specs mit Jira synchronisiert und Rail-Ergebnisse aktualisieren die verknüpften Vorgänge.`,pausedHelp:`Die Synchronisierung ist pausiert. Specs bleiben auf dem Board, aktualisieren Jira aber nicht mehr.`,syncNow:`Jetzt synchronisieren`,syncing:`Synchronisiere…`,syncedToast:`{{count}} Vorgang/Vorgänge aus Jira synchronisiert`,disconnect:`Trennen`,disconnectConfirm:`Dieses Projekt von Jira trennen? Bestehende Specs bleiben auf dem Board; die Synchronisierung stoppt.`,disconnectedToast:`Von Jira getrennt`},s={title:`Ausstehende Jira-Updates`,pending:`{{count}} ausstehend`,dead:`{{count}} benötigen Aufmerksamkeit`,allSynced:`Alle Updates synchronisiert`,retry:`Erneut versuchen`,deadHelp:`Diese konnten nicht automatisch angewendet werden (z. B. hat dein Workflow keinen passenden Übergang). Korrigiere in Jira und versuche es erneut.`,opTransition:`Status-Update`,opComment:`Kommentar`,opCreate:`Vorgang erstellen`},c={title:`Jira getrennt`,body:`Dein Jira-Token ist abgelaufen oder wurde widerrufen. Verbinde dich erneut, um {{count}} ausstehende(s) Update(s) zu synchronisieren.`,reconnect:`Erneut verbinden`},l={label:`Jira`,openInJira:`In Jira öffnen`},u={willCreate:`Wird in Jira erstellt · {{key}}`},d={saveTitle:`Jira-Vorgang aktualisieren`,saveBody:`Beim Speichern wird der Jira-Vorgang {{key}} mit diesen Änderungen aktualisiert.`,saveConfirm:`Speichern und Jira aktualisieren`,detailsTitle:`Jira-Details`,developmentTitle:`Entwicklung`,dev:{pullRequests:`Pull Requests`,branches:`Branches`,commits:`Commits`},goToTicket:`Zum Ticket {{key}}`,epic:`Epic`},f={moveButton:`Verschieben nach {{status}}`,title:`Spec nach {{status}} verschieben?`,body:`Das Jira-Ticket {{key}} dieser Spec wird nach „{{status}}“ verschoben, statt gelöscht zu werden. Optional einen Grund angeben.`,commentLabel:`Grund (optional)`,commentPlaceholder:`Warum verschiebst du diese Spec?`,confirm:`Nach {{status}} verschieben`,moving:`Wird verschoben…`,movedToast:`{{key}} nach {{status}} verschoben`,configLabel:`Beim Verwerfen Ticket verschieben nach`,configHelp:`Wenn du eine mit Jira verknüpfte Spec verwirfst, wird ihr Ticket in diesen Status verschoben, statt gelöscht zu werden.`,configNone:`Keiner — normal löschen`,reviewLabel:`Verwerfen verschiebt nach`},p={generic:`Etwas ist schiefgelaufen`,testFirst:`Teste zuerst die Verbindung`},m={queued:`Verschieben nach „{{status}}" eingereiht — Synchronisierung mit Jira`,failed:`Spec konnte in Jira nicht verschoben werden`},h={section:e,wizard:t,creds:n,project:r,mapping:i,review:a,status:o,outbox:s,authExpired:c,badge:l,addSpec:u,detail:d,discard:f,errors:p,moveToStatus:m};export{u as addSpec,c as authExpired,l as badge,n as creds,h as default,d as detail,f as discard,p as errors,i as mapping,m as moveToStatus,s as outbox,r as project,a as review,e as section,o as status,t as wizard};
@@ -1 +0,0 @@
1
- var e={title:`Jira`,subtitle:`このプロジェクトのスペックを Jira ボードと同期します`,connectCta:`Jira に接続`},t={step:`ステップ {{current}} / {{total}}`,step1Title:`Jira アカウントに接続`,step2Title:`Jira プロジェクトを選択`,step3Title:`ステータスを対応付け(任意)`,step4Title:`確認して接続`,next:`次へ`,back:`戻る`,cancel:`キャンセル`,connect:`接続`,connecting:`接続中…`,later:`後で行う`},n={baseUrlLabel:`Jira の URL`,baseUrlPlaceholder:`https://your-company.atlassian.net`,baseUrlHelp:`Jira Cloud サイトまたは Data Center サーバーの URL。`,emailLabel:`アカウントのメール`,emailPlaceholder:`you@company.com`,emailHelp:`Jira Cloud では必須です — Atlassian アカウントのメールアドレス。`,tokenLabel:`API トークン`,tokenHelp:`id.atlassian.com → セキュリティ → API トークンで作成します。このマシンに暗号化して保存され、外部に出ることはありません。`,test:`接続をテスト`,testing:`テスト中…`,testOk:`{{name}} として接続しました`,retest:`再テスト`},r={searchPlaceholder:`プロジェクトを検索…`,manualLabel:`またはプロジェクトキーを入力`,manualPlaceholder:`PROJ`,selected:`{{key}} — {{name}}`,none:`プロジェクトが見つかりません`,loading:`プロジェクトを読み込み中…`},i={intro:`ステータスはカテゴリ(To Do / 進行中 / 完了)で自動的に対応付けられます。ワークフローが独自の名前を使う場合は上書きできます。`,auto:`自動(カテゴリ別)`,todo:`バックログ / To Do`,inProgress:`進行中`,onReview:`レビュー中`,done:`完了`,cancelled:`キャンセル`,loading:`ステータスを読み込み中…`},a={site:`サイト`,project:`プロジェクト`,account:`アカウント`,type:`種類`,cloud:`Jira Cloud`,dc:`Jira Data Center`},o={connected:`{{key}} に接続済み`,enabledLabel:`同期を有効化`,enabledHelp:`有効にすると、スペックが Jira と同期し、rail の結果がリンクされた課題を更新します。`,pausedHelp:`同期は一時停止中です。スペックはボードに残りますが、Jira は更新されなくなります。`,syncNow:`今すぐ同期`,syncing:`同期中…`,syncedToast:`Jira から {{count}} 件の課題を同期しました`,disconnect:`切断`,disconnectConfirm:`このプロジェクトを Jira から切断しますか?既存のスペックはボードに残り、同期は停止します。`,disconnectedToast:`Jira から切断しました`},s={title:`保留中の Jira 更新`,pending:`{{count}} 件保留中`,dead:`{{count}} 件は対応が必要`,allSynced:`すべての更新が同期されました`,retry:`再試行`,deadHelp:`これらは自動で適用できませんでした(例:ワークフローに一致する遷移がない)。Jira で修正してから再試行してください。`,opTransition:`ステータス更新`,opComment:`コメント`,opCreate:`課題を作成`},c={title:`Jira が切断されました`,body:`Jira トークンが失効または取り消されました。再接続して保留中の {{count}} 件の更新を同期してください。`,reconnect:`再接続`},l={label:`Jira`,openInJira:`Jira で開く`},u={willCreate:`Jira に作成されます · {{key}}`},d={saveTitle:`Jira 課題を更新`,saveBody:`保存すると、Jira 課題 {{key}} がこの変更で更新されます。`,saveConfirm:`保存して Jira を更新`,detailsTitle:`Jira の詳細`,developmentTitle:`開発`,dev:{pullRequests:`プルリクエスト`,branches:`ブランチ`,commits:`コミット`},goToTicket:`チケット {{key}} を開く`,epic:`エピック`},f={moveButton:`{{status}} に移動`,title:`仕様を {{status}} に移動しますか?`,body:`この仕様の Jira 課題 {{key}} は削除されず、「{{status}}」に移動されます。任意で理由を追加できます。`,commentLabel:`理由(任意)`,commentPlaceholder:`この仕様を移動する理由は?`,confirm:`{{status}} に移動`,moving:`移動中…`,movedToast:`{{key}} を {{status}} に移動しました`,configLabel:`破棄時に課題を移動する先`,configHelp:`Jira と連携した仕様を破棄すると、その課題は削除されず、このステータスに移動されます。`,configNone:`なし — 通常どおり削除`,reviewLabel:`破棄時の移動先`},p={generic:`問題が発生しました`,testFirst:`先に接続をテストしてください`},m={queued:`「{{status}}」への移動をキューに追加 — Jira と同期中`,failed:`Jira でのスペック移動に失敗しました`},h={section:e,wizard:t,creds:n,project:r,mapping:i,review:a,status:o,outbox:s,authExpired:c,badge:l,addSpec:u,detail:d,discard:f,errors:p,moveToStatus:m};export{u as addSpec,c as authExpired,l as badge,n as creds,h as default,d as detail,f as discard,p as errors,i as mapping,m as moveToStatus,s as outbox,r as project,a as review,e as section,o as status,t as wizard};
@@ -1 +0,0 @@
1
- import{r as e}from"./rolldown-runtime-CNC7AqOf.js";var t=e({addSpec:()=>f,authExpired:()=>u,badge:()=>d,creds:()=>i,default:()=>_,detail:()=>p,discard:()=>m,errors:()=>h,mapping:()=>o,moveToStatus:()=>g,outbox:()=>l,project:()=>a,review:()=>s,section:()=>n,status:()=>c,wizard:()=>r}),n={title:`Jira`,subtitle:`Sync this project's specs with a Jira board`,connectCta:`Connect to Jira`},r={step:`Step {{current}} of {{total}}`,step1Title:`Connect your Jira account`,step2Title:`Choose a Jira project`,step3Title:`Map statuses (optional)`,step4Title:`Review & connect`,next:`Next`,back:`Back`,cancel:`Cancel`,connect:`Connect`,connecting:`Connecting…`,later:`Do it later`},i={baseUrlLabel:`Jira URL`,baseUrlPlaceholder:`https://your-company.atlassian.net`,baseUrlHelp:`Your Jira Cloud site or Data Center server URL.`,emailLabel:`Account email`,emailPlaceholder:`you@company.com`,emailHelp:`Required for Jira Cloud — the email of your Atlassian account.`,tokenLabel:`API token`,tokenHelp:`Create one at id.atlassian.com → Security → API tokens. It is stored encrypted on this machine and never leaves it.`,test:`Test connection`,testing:`Testing…`,testOk:`Connected as {{name}}`,retest:`Re-test`},a={searchPlaceholder:`Search projects…`,manualLabel:`Or enter a project key`,manualPlaceholder:`PROJ`,selected:`{{key}} — {{name}}`,none:`No projects found`,loading:`Loading projects…`},o={intro:`Statuses are matched automatically by category (To Do / In Progress / Done). Override any state if your workflow uses custom names.`,auto:`Automatic (by category)`,todo:`Backlog / To Do`,inProgress:`In Progress`,onReview:`On Review`,done:`Done`,cancelled:`Cancelled`,loading:`Loading statuses…`},s={site:`Site`,project:`Project`,account:`Account`,type:`Type`,cloud:`Jira Cloud`,dc:`Jira Data Center`},c={connected:`Connected to {{key}}`,enabledLabel:`Sync enabled`,enabledHelp:`When on, specs sync with Jira and rail outcomes update the linked issues.`,pausedHelp:`Sync is paused. Specs stay on the board but no longer update Jira.`,syncNow:`Sync now`,syncing:`Syncing…`,syncedToast:`Synced {{count}} issue(s) from Jira`,disconnect:`Disconnect`,disconnectConfirm:`Disconnect this project from Jira? Existing specs stay on the board; sync stops.`,disconnectedToast:`Disconnected from Jira`},l={title:`Pending Jira updates`,pending:`{{count}} pending`,dead:`{{count}} need attention`,allSynced:`All updates synced`,retry:`Retry`,deadHelp:`These couldn't be applied automatically (e.g. your workflow has no matching transition). Fix in Jira, then retry.`,opTransition:`Status update`,opComment:`Comment`,opCreate:`Create issue`},u={title:`Jira disconnected`,body:`Your Jira token expired or was revoked. Reconnect to sync {{count}} pending update(s).`,reconnect:`Reconnect`},d={label:`Jira`,openInJira:`Open in Jira`},f={willCreate:`Will be created in Jira · {{key}}`},p={saveTitle:`Update Jira ticket`,saveBody:`Saving will update Jira issue {{key}} with these changes.`,saveConfirm:`Save & update Jira`,detailsTitle:`Jira details`,developmentTitle:`Development`,dev:{pullRequests:`Pull requests`,branches:`Branches`,commits:`Commits`},goToTicket:`Go to Ticket {{key}}`,epic:`Epic`},m={moveButton:`Move to {{status}}`,title:`Move spec to {{status}}?`,body:`This spec's Jira issue {{key}} will be moved to "{{status}}" instead of being deleted. Add an optional reason for the move.`,commentLabel:`Reason (optional)`,commentPlaceholder:`Why are you moving this spec?`,confirm:`Move to {{status}}`,moving:`Moving…`,movedToast:`Moved {{key}} to {{status}}`,configLabel:`On discard, move issue to`,configHelp:`When you discard a Jira-backed spec, its issue is moved to this status instead of being deleted.`,configNone:`None — delete normally`,reviewLabel:`Discard moves to`},h={generic:`Something went wrong`,testFirst:`Test the connection first`},g={queued:`Move to "{{status}}" queued — syncing with Jira`,failed:`Failed to move the spec in Jira`},_={section:n,wizard:r,creds:i,project:a,mapping:o,review:s,status:c,outbox:l,authExpired:u,badge:d,addSpec:f,detail:p,discard:m,errors:h,moveToStatus:g};export{t};
@@ -1 +0,0 @@
1
- var e={title:`Jira`,subtitle:`Sincronize as specs deste projeto com um quadro Jira`,connectCta:`Conectar ao Jira`},t={step:`Passo {{current}} de {{total}}`,step1Title:`Conecte a sua conta Jira`,step2Title:`Escolha um projeto Jira`,step3Title:`Mapear estados (opcional)`,step4Title:`Revisar e conectar`,next:`Seguinte`,back:`Voltar`,cancel:`Cancelar`,connect:`Conectar`,connecting:`A conectar…`,later:`Fazer mais tarde`},n={baseUrlLabel:`URL do Jira`,baseUrlPlaceholder:`https://sua-empresa.atlassian.net`,baseUrlHelp:`O URL do seu site Jira Cloud ou servidor Data Center.`,emailLabel:`E-mail da conta`,emailPlaceholder:`voce@empresa.com`,emailHelp:`Necessário para o Jira Cloud — o e-mail da sua conta Atlassian.`,tokenLabel:`Token de API`,tokenHelp:`Crie um em id.atlassian.com → Segurança → Tokens de API. É armazenado cifrado nesta máquina e nunca a abandona.`,test:`Testar ligação`,testing:`A testar…`,testOk:`Ligado como {{name}}`,retest:`Testar de novo`},r={searchPlaceholder:`Procurar projetos…`,manualLabel:`Ou introduza uma chave de projeto`,manualPlaceholder:`PROJ`,selected:`{{key}} — {{name}}`,none:`Nenhum projeto encontrado`,loading:`A carregar projetos…`},i={intro:`Os estados são associados automaticamente por categoria (A fazer / Em curso / Concluído). Substitua um estado se o seu fluxo de trabalho usar nomes personalizados.`,auto:`Automático (por categoria)`,todo:`Backlog / A fazer`,inProgress:`Em curso`,onReview:`Em revisão`,done:`Concluído`,cancelled:`Cancelado`,loading:`A carregar estados…`},a={site:`Site`,project:`Projeto`,account:`Conta`,type:`Tipo`,cloud:`Jira Cloud`,dc:`Jira Data Center`},o={connected:`Ligado a {{key}}`,enabledLabel:`Sincronização ativada`,enabledHelp:`Quando ativa, as specs sincronizam com o Jira e os resultados dos rails atualizam os tickets associados.`,pausedHelp:`A sincronização está em pausa. As specs permanecem no quadro mas já não atualizam o Jira.`,syncNow:`Sincronizar agora`,syncing:`A sincronizar…`,syncedToast:`{{count}} ticket(s) sincronizados do Jira`,disconnect:`Desligar`,disconnectConfirm:`Desligar este projeto do Jira? As specs existentes permanecem no quadro; a sincronização para.`,disconnectedToast:`Desligado do Jira`},s={title:`Atualizações Jira pendentes`,pending:`{{count}} pendentes`,dead:`{{count}} a precisar de atenção`,allSynced:`Todas as atualizações sincronizadas`,retry:`Tentar de novo`,deadHelp:`Estas não puderam ser aplicadas automaticamente (ex.: o seu fluxo de trabalho não tem uma transição correspondente). Corrija no Jira e tente de novo.`,opTransition:`Atualização de estado`,opComment:`Comentário`,opCreate:`Criar ticket`},c={title:`Jira desligado`,body:`O seu token Jira expirou ou foi revogado. Volte a ligar para sincronizar {{count}} atualizações pendentes.`,reconnect:`Voltar a ligar`},l={label:`Jira`,openInJira:`Abrir no Jira`},u={willCreate:`Será criado no Jira · {{key}}`},d={saveTitle:`Atualizar ticket do Jira`,saveBody:`Ao salvar, o ticket do Jira {{key}} será atualizado com estas alterações.`,saveConfirm:`Salvar e atualizar Jira`,detailsTitle:`Detalhes do Jira`,developmentTitle:`Desenvolvimento`,dev:{pullRequests:`Pull requests`,branches:`Ramos`,commits:`Commits`},goToTicket:`Ir para o ticket {{key}}`,epic:`Épico`},f={moveButton:`Mover para {{status}}`,title:`Mover a spec para {{status}}?`,body:`O ticket do Jira {{key}} desta spec será movido para "{{status}}" em vez de ser excluído. Adicione um motivo opcional.`,commentLabel:`Motivo (opcional)`,commentPlaceholder:`Por que está movendo esta spec?`,confirm:`Mover para {{status}}`,moving:`Movendo…`,movedToast:`{{key}} movido para {{status}}`,configLabel:`Ao descartar, mover o ticket para`,configHelp:`Quando você descarta uma spec vinculada ao Jira, o ticket é movido para este status em vez de ser excluído.`,configNone:`Nenhum — excluir normalmente`,reviewLabel:`Descartar move para`},p={generic:`Algo correu mal`,testFirst:`Teste primeiro a ligação`},m={queued:`Movimento para "{{status}}" em fila — a sincronizar com o Jira`,failed:`Não foi possível mover a spec no Jira`},h={section:e,wizard:t,creds:n,project:r,mapping:i,review:a,status:o,outbox:s,authExpired:c,badge:l,addSpec:u,detail:d,discard:f,errors:p,moveToStatus:m};export{u as addSpec,c as authExpired,l as badge,n as creds,h as default,d as detail,f as discard,p as errors,i as mapping,m as moveToStatus,s as outbox,r as project,a as review,e as section,o as status,t as wizard};
@@ -1 +0,0 @@
1
- var e={title:`Jira`,subtitle:`将此项目的规格与 Jira 看板同步`,connectCta:`连接到 Jira`},t={step:`第 {{current}} 步,共 {{total}} 步`,step1Title:`连接你的 Jira 账户`,step2Title:`选择一个 Jira 项目`,step3Title:`映射状态(可选)`,step4Title:`检查并连接`,next:`下一步`,back:`返回`,cancel:`取消`,connect:`连接`,connecting:`正在连接…`,later:`稍后再做`},n={baseUrlLabel:`Jira 网址`,baseUrlPlaceholder:`https://your-company.atlassian.net`,baseUrlHelp:`你的 Jira Cloud 站点或 Data Center 服务器网址。`,emailLabel:`账户邮箱`,emailPlaceholder:`you@company.com`,emailHelp:`Jira Cloud 必填——你的 Atlassian 账户邮箱。`,tokenLabel:`API 令牌`,tokenHelp:`在 id.atlassian.com → 安全 → API 令牌中创建。它会加密存储在本机,永不外泄。`,test:`测试连接`,testing:`测试中…`,testOk:`已连接为 {{name}}`,retest:`重新测试`},r={searchPlaceholder:`搜索项目…`,manualLabel:`或输入项目键`,manualPlaceholder:`PROJ`,selected:`{{key}} — {{name}}`,none:`未找到项目`,loading:`正在加载项目…`},i={intro:`状态会按类别自动匹配(待办 / 进行中 / 完成)。如果你的工作流使用自定义名称,可覆盖任一状态。`,auto:`自动(按类别)`,todo:`待办 / To Do`,inProgress:`进行中`,onReview:`评审中`,done:`完成`,cancelled:`已取消`,loading:`正在加载状态…`},a={site:`站点`,project:`项目`,account:`账户`,type:`类型`,cloud:`Jira Cloud`,dc:`Jira Data Center`},o={connected:`已连接到 {{key}}`,enabledLabel:`已启用同步`,enabledHelp:`启用后,规格会与 Jira 同步,rail 的结果会更新关联的事务。`,pausedHelp:`同步已暂停。规格仍在看板上,但不再更新 Jira。`,syncNow:`立即同步`,syncing:`正在同步…`,syncedToast:`已从 Jira 同步 {{count}} 个事务`,disconnect:`断开连接`,disconnectConfirm:`要断开此项目与 Jira 的连接吗?现有规格仍保留在看板上;同步将停止。`,disconnectedToast:`已断开与 Jira 的连接`},s={title:`待处理的 Jira 更新`,pending:`{{count}} 个待处理`,dead:`{{count}} 个需要处理`,allSynced:`所有更新已同步`,retry:`重试`,deadHelp:`这些无法自动应用(例如你的工作流没有匹配的转换)。请在 Jira 中修正后重试。`,opTransition:`状态更新`,opComment:`评论`,opCreate:`创建事务`},c={title:`Jira 已断开`,body:`你的 Jira 令牌已过期或被吊销。请重新连接以同步 {{count}} 个待处理更新。`,reconnect:`重新连接`},l={label:`Jira`,openInJira:`在 Jira 中打开`},u={willCreate:`将在 Jira 中创建 · {{key}}`},d={saveTitle:`更新 Jira 工单`,saveBody:`保存后将用这些更改更新 Jira 工单 {{key}}。`,saveConfirm:`保存并更新 Jira`,detailsTitle:`Jira 详情`,developmentTitle:`开发`,dev:{pullRequests:`拉取请求`,branches:`分支`,commits:`提交`},goToTicket:`前往工单 {{key}}`,epic:`史诗`},f={moveButton:`移动到 {{status}}`,title:`将规格移动到 {{status}}?`,body:`此规格的 Jira 工单 {{key}} 将被移动到“{{status}}”,而不是被删除。可选择填写原因。`,commentLabel:`原因(可选)`,commentPlaceholder:`为什么要移动此规格?`,confirm:`移动到 {{status}}`,moving:`正在移动…`,movedToast:`已将 {{key}} 移动到 {{status}}`,configLabel:`丢弃时将工单移动到`,configHelp:`当你丢弃一个关联 Jira 的规格时,其工单将被移动到此状态,而不是被删除。`,configNone:`无 — 正常删除`,reviewLabel:`丢弃时移动到`},p={generic:`出了点问题`,testFirst:`请先测试连接`},m={queued:`移动到"{{status}}"已排队 — 正在与 Jira 同步`,failed:`在 Jira 中移动该 spec 失败`},h={section:e,wizard:t,creds:n,project:r,mapping:i,review:a,status:o,outbox:s,authExpired:c,badge:l,addSpec:u,detail:d,discard:f,errors:p,moveToStatus:m};export{u as addSpec,c as authExpired,l as badge,n as creds,h as default,d as detail,f as discard,p as errors,i as mapping,m as moveToStatus,s as outbox,r as project,a as review,e as section,o as status,t as wizard};
@@ -1 +0,0 @@
1
- var e={title:`Jira`,subtitle:`Sincronizza le spec di questo progetto con una board Jira`,connectCta:`Connetti a Jira`},t={step:`Passo {{current}} di {{total}}`,step1Title:`Connetti il tuo account Jira`,step2Title:`Scegli un progetto Jira`,step3Title:`Mappa gli stati (facoltativo)`,step4Title:`Rivedi e connetti`,next:`Avanti`,back:`Indietro`,cancel:`Annulla`,connect:`Connetti`,connecting:`Connessione…`,later:`Fallo più tardi`},n={baseUrlLabel:`URL Jira`,baseUrlPlaceholder:`https://tua-azienda.atlassian.net`,baseUrlHelp:`L'URL del tuo sito Jira Cloud o server Data Center.`,emailLabel:`Email dell'account`,emailPlaceholder:`tu@azienda.com`,emailHelp:`Richiesta per Jira Cloud — l'email del tuo account Atlassian.`,tokenLabel:`Token API`,tokenHelp:`Creane uno su id.atlassian.com → Sicurezza → Token API. Viene memorizzato cifrato su questa macchina e non la lascia mai.`,test:`Prova connessione`,testing:`Verifica…`,testOk:`Connesso come {{name}}`,retest:`Riprova`},r={searchPlaceholder:`Cerca progetti…`,manualLabel:`Oppure inserisci una chiave progetto`,manualPlaceholder:`PROJ`,selected:`{{key}} — {{name}}`,none:`Nessun progetto trovato`,loading:`Caricamento progetti…`},i={intro:`Gli stati vengono associati automaticamente per categoria (Da fare / In corso / Fatto). Sovrascrivi uno stato se il tuo workflow usa nomi personalizzati.`,auto:`Automatico (per categoria)`,todo:`Backlog / Da fare`,inProgress:`In corso`,onReview:`In revisione`,done:`Fatto`,cancelled:`Annullato`,loading:`Caricamento stati…`},a={site:`Sito`,project:`Progetto`,account:`Account`,type:`Tipo`,cloud:`Jira Cloud`,dc:`Jira Data Center`},o={connected:`Connesso a {{key}}`,enabledLabel:`Sincronizzazione attiva`,enabledHelp:`Quando attiva, le spec si sincronizzano con Jira e gli esiti dei rail aggiornano i ticket collegati.`,pausedHelp:`La sincronizzazione è in pausa. Le spec restano sulla board ma non aggiornano più Jira.`,syncNow:`Sincronizza ora`,syncing:`Sincronizzazione…`,syncedToast:`{{count}} ticket sincronizzati da Jira`,disconnect:`Disconnetti`,disconnectConfirm:`Disconnettere questo progetto da Jira? Le spec esistenti restano sulla board; la sincronizzazione si ferma.`,disconnectedToast:`Disconnesso da Jira`},s={title:`Aggiornamenti Jira in sospeso`,pending:`{{count}} in sospeso`,dead:`{{count}} da verificare`,allSynced:`Tutti gli aggiornamenti sincronizzati`,retry:`Riprova`,deadHelp:`Questi non sono stati applicati automaticamente (es. il tuo workflow non ha una transizione corrispondente). Correggi in Jira, poi riprova.`,opTransition:`Aggiornamento stato`,opComment:`Commento`,opCreate:`Crea ticket`},c={title:`Jira disconnesso`,body:`Il tuo token Jira è scaduto o è stato revocato. Riconnettiti per sincronizzare {{count}} aggiornamenti in sospeso.`,reconnect:`Riconnetti`},l={label:`Jira`,openInJira:`Apri in Jira`},u={willCreate:`Verrà creato in Jira · {{key}}`},d={saveTitle:`Aggiorna il ticket Jira`,saveBody:`Salvando, il ticket Jira {{key}} verrà aggiornato con queste modifiche.`,saveConfirm:`Salva e aggiorna Jira`,detailsTitle:`Dettagli Jira`,developmentTitle:`Sviluppo`,dev:{pullRequests:`Pull request`,branches:`Rami`,commits:`Commit`},goToTicket:`Vai al ticket {{key}}`,epic:`Epica`},f={moveButton:`Sposta in {{status}}`,title:`Spostare la spec in {{status}}?`,body:`Il ticket Jira {{key}} di questa spec verrà spostato in "{{status}}" invece di essere eliminato. Aggiungi un motivo facoltativo.`,commentLabel:`Motivo (facoltativo)`,commentPlaceholder:`Perché stai spostando questa spec?`,confirm:`Sposta in {{status}}`,moving:`Spostamento…`,movedToast:`{{key}} spostato in {{status}}`,configLabel:`Allo scarto, sposta il ticket in`,configHelp:`Quando scarti una spec collegata a Jira, il suo ticket viene spostato in questo stato invece di essere eliminato.`,configNone:`Nessuno — elimina normalmente`,reviewLabel:`Lo scarto sposta in`},p={generic:`Qualcosa è andato storto`,testFirst:`Prova prima la connessione`},m={queued:`Spostamento in "{{status}}" in coda — sincronizzazione con Jira`,failed:`Impossibile spostare la spec in Jira`},h={section:e,wizard:t,creds:n,project:r,mapping:i,review:a,status:o,outbox:s,authExpired:c,badge:l,addSpec:u,detail:d,discard:f,errors:p,moveToStatus:m};export{u as addSpec,c as authExpired,l as badge,n as creds,h as default,d as detail,f as discard,p as errors,i as mapping,m as moveToStatus,s as outbox,r as project,a as review,e as section,o as status,t as wizard};