specrails-desktop 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/dist/assets/{ActivityFeedPage-DNqnf1fZ.js → ActivityFeedPage-qPnIozX9.js} +1 -1
- package/client/dist/assets/{AgentsPage-vmNIEbGM.js → AgentsPage-B_8WZUUI.js} +1 -1
- package/client/dist/assets/{AnalyticsPage-CdfN0ofZ.js → AnalyticsPage-DCnFtl8E.js} +1 -1
- package/client/dist/assets/{BarChart-CIkopHjl.js → BarChart-Cr7__BAU.js} +1 -1
- package/client/dist/assets/{CodePage-DDRNU5FN.js → CodePage-C7uJgpF8.js} +1 -1
- package/client/dist/assets/{DesktopAnalyticsPage-Cl3sKKSG.js → DesktopAnalyticsPage-DqCEHy0T.js} +1 -1
- package/client/dist/assets/{DocsDialog-BGrBOfUr.js → DocsDialog-C_jIYef_.js} +1 -1
- package/client/dist/assets/{DocsPage-CY-2SSzw.js → DocsPage-CTifuwaa.js} +1 -1
- package/client/dist/assets/{ExportDropdown-BRHcvP0r.js → ExportDropdown-BcmU6I16.js} +1 -1
- package/client/dist/assets/{IntegrationsPage-nKdLB4Ub.js → IntegrationsPage-DZntBwnd.js} +1 -1
- package/client/dist/assets/{JobDetailPage-Bf0A6WWQ.js → JobDetailPage-4ncqbKxF.js} +1 -1
- package/client/dist/assets/{JobsPage-Vg4nXPdL.js → JobsPage-CapLnvIs.js} +1 -1
- package/client/dist/assets/{dist-js-CUs5GjwA.js → dist-js-DKGy2gD-.js} +1 -1
- package/client/dist/assets/{dist-js-0i_klubI.js → dist-js-DvPNad3t.js} +1 -1
- package/client/dist/assets/index-Cc3LCzBq.css +2 -0
- package/client/dist/assets/{index-BXoHFtfG.js → index-IgMtikGD.js} +45 -45
- package/client/dist/assets/{lib-D6M_MvoC.js → lib-BouqFmKc.js} +1 -1
- package/client/dist/assets/{settings-BRaLLSVi.js → settings-BwMQ1lx5.js} +1 -1
- package/client/dist/assets/{settings-BI_cVCqN.js → settings-CewqtD9V.js} +1 -1
- package/client/dist/assets/{settings-BcqH0oea.js → settings-CqCY8gZq.js} +1 -1
- package/client/dist/assets/settings-Cx97tHs5.js +1 -0
- package/client/dist/assets/{settings-pT3MzfRu.js → settings-CyixAx0X.js} +1 -1
- package/client/dist/assets/{settings-GOBKOTGl.js → settings-DkB9Wh-f.js} +1 -1
- package/client/dist/assets/{settings-D6QMBlGQ.js → settings-EMaKBwE0.js} +1 -1
- package/client/dist/assets/{settings-u-16ISHt.js → settings-Puea5c8v.js} +1 -1
- package/client/dist/assets/{tickets-9kdPXInd.js → tickets-CG_mo-Bg.js} +1 -1
- package/client/dist/assets/{tickets-n23kDqJT.js → tickets-CVJQ-vRm.js} +1 -1
- package/client/dist/assets/{tickets-tGx5AR5b.js → tickets-D5MSAPe_.js} +1 -1
- package/client/dist/assets/{tickets-1UIGf_oA.js → tickets-DBV3wgQZ.js} +1 -1
- package/client/dist/assets/{tickets-DNmXcAwu.js → tickets-Q0_pONEh.js} +1 -1
- package/client/dist/assets/{tickets-C6pwZwt4.js → tickets-RZ0LyeQe.js} +1 -1
- package/client/dist/assets/{tickets-DAjtxAVb.js → tickets-d1A6EOHa.js} +1 -1
- package/client/dist/assets/{tickets-0rM0lIXd.js → tickets-r4-oNV0R.js} +1 -1
- package/client/dist/assets/{useProjectCache-BeyBSNpD.js → useProjectCache-vvFIixVa.js} +1 -1
- package/client/dist/index.html +4 -4
- package/package.json +1 -1
- package/server/dist/core-update-manager.js +219 -0
- package/server/dist/desktop-router.js +43 -0
- package/server/dist/framework-manager.js +48 -10
- package/server/dist/path-resolver.js +21 -0
- package/server/dist/semver-lite.js +92 -0
- package/server/dist/setup-manager.js +8 -1
- package/client/dist/assets/index-D6BaYRRU.css +0 -2
- package/client/dist/assets/settings-C0-7Fpxg.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={title:`语言`,description:`Desktop 全局界面语言。即时生效,无需重启。`,selectLabel:`界面语言`,updateFailed:`无法保存语言偏好`},t={title:`项目设置`},n={saveFailed:`保存失败:{{message}}`},r={title:`流水线遥测`,description:`采集 token 用量、阶段耗时和子 Agent 活动以供诊断导出。默认关闭。`,toggleLabel:`启用流水线遥测`,toggleDescription:`开启后,流水线任务的 OTEL 数据将在本地采集。在任意任务卡片上使用<mono>导出诊断</mono>按钮即可下载。`,enabled:`已启用流水线遥测`,disabled:`已禁用流水线遥测`,saveFailed:`保存遥测设置失败`},i={title:`Rail 前置提示词`,description:`追加到 implement 与 batch-implement rail 任务中的项目专属附加指令,位于工单上下文之后、开始执行之前。`,label:`前置提示词`,placeholder:`示例:优先采用增量修改,保持迁移向后兼容,并为每次 rail 变更添加测试。`,helper:`用于应随每次 rail 实现运行附带的稳定项目指引。`,saveButton:`保存前置提示词`,cleared:`前置提示词已清除`,saved:`前置提示词已保存`,saveFailed:`保存前置提示词失败`},a={title:`Ultracode 前置提示词`,description:`在 Ultracode(仅限 Claude 的 rail)模式下发送给 Claude 的指令。Ultracode 跳过 OpenSpec 流水线——直接把该前置提示词加上 spec 文本交给 Claude,让其自主实现。留空则使用内置默认值。`,label:`Ultra 前置提示词`,placeholder:`留空以使用默认的 Ultracode 指令。`,helper:`spec 文本会自动追加在该前置提示词之后。留空 = 使用默认值。`,saveButton:`保存 Ultra 前置提示词`,resetToDefault:`Ultra 前置提示词已重置为默认值`,saved:`Ultra 前置提示词已保存`,saveFailed:`保存 Ultra 前置提示词失败`},o={title:`预算`,description:`为此项目设置每日支出上限。达到上限时队列自动暂停。`,dailyLabel:`每日预算(USD)`,dailyHelper:`留空表示禁用。支出按最近 24 小时计算。`,dailyPlaceholder:`如 5.00`,perJobLabel:`单任务成本告警(USD)`,perJobHelper:`当此项目中的单个任务超过此金额时告警。`,perJobPlaceholder:`如 0.50`,invalidNumber:`请输入正数,或留空以禁用`,dailyRemoved:`已移除每日预算`,dailySet:"每日预算已设为 ${{amount}}",perJobAlertDisabled:`已禁用单任务成本告警`,alertSet:"已设置任务超过 ${{amount}} 时告警",saveBudgetFailed:`保存预算失败`,saveThresholdFailed:`保存阈值失败`},s={title:`Desktop 设置`,description:`管理已注册的项目并查看 Desktop 信息。`,registeredProjects:`已注册项目`,noProjects:`尚未注册任何项目`,techUrlDescription:`specrails-tech API 的基础 URL(默认:http://localhost:3000)`,techUrlSaved:`specrails-tech URL 已保存`,techUrlSaveFailed:`保存 URL 失败`,budgetAlertsHeading:`预算与告警`,dailyBudgetLabel:`Desktop 每日预算(USD)`,dailyBudgetHelper:`所有项目的全局每日支出上限。超出后队列自动暂停。`,dailyBudgetPlaceholder:`如 10.00`,perJobHelper:`当单个任务超过此金额时告警。留空表示禁用。`,costAlertsDisabled:`已禁用成本告警`,dailyBudgetRemoved:`已移除 Desktop 每日预算`,dailyBudgetSet:"Desktop 每日预算已设为 ${{amount}}",dailyBudgetSaveFailed:`保存 Desktop 每日预算失败`,projectRemoved:`项目已移除`,projectRemoveFailed:`移除项目失败`,onboardingHeading:`新手引导`,platformTour:`平台导览`,platformTourDescription:`重播欢迎向导,回顾核心功能。`,replayTour:`重播导览`,terminalPanelHeading:`终端面板`,infoHeading:`Desktop 信息`,infoPort:`端口`,infoProjects:`项目`,infoDb:`Desktop 数据库`},c={heading:`系统通知`,description:`任务完成或失败时显示原生桌面通知。仅在标签页未聚焦时显示通知。`,enableLabel:`启用系统通知`,notifyOn:`通知时机:`,filterAll:`全部(完成与失败)`,filterCompleted:`仅完成`,filterFailed:`仅失败`,enabledToast:`已启用系统通知`,disabledToast:`已禁用系统通知`},l={heading:`出站 Webhook`,description:`在 Desktop 事件发生时通知外部工具(Slack、Zapier、CI/CD)。设置密钥后,请求会通过 <code>X-Specrails-Signature</code> 进行签名。`,eventJobCompleted:`任务完成`,eventJobFailed:`任务失败`,eventDailyBudgetExceeded:`超出每日预算`,statusOn:`开`,statusOff:`关`,disable:`禁用`,enable:`启用`,sendTestPing:`发送测试 ping`,addHeading:`添加 Webhook`,secretPlaceholder:`签名密钥(可选)`,addButton:`添加 Webhook`,urlRequired:`URL 为必填项`,selectEvent:`请至少选择一个事件`,added:`Webhook 已添加`,addFailed:`添加 Webhook 失败`,updateFailed:`更新 Webhook 失败`,removed:`Webhook 已移除`,removeFailed:`移除 Webhook 失败`,testPingSent:`测试 ping 已发送`,testPingFailed:`发送测试 ping 失败`},u={title:`终端面板`,desktopDescription:`Desktop 全局默认值,应用于所有项目,除非设置了项目级覆盖。`,projectDescription:`终端面板的项目级覆盖。保持字段不变即继承 Desktop 默认值。`,fontFamily:`字体`,fontSize:`字号({{min}}–{{max}})`,renderMode:`渲染模式`,copyOnSelect:`选中即复制`,shellIntegration:`Shell 集成(OSC 133 标记)`,notifyLongRunning:`长耗时命令通知`,longCommandThreshold:`长命令阈值(ms)`,imageRendering:`内联图片渲染(Sixel + iTerm2)`,browserShortcutUrl:`浏览器快捷方式 URL`,quickScript:`快捷脚本(粘贴到活动终端——需手动按 Enter)`,reset:`重置`,unsavedChanges:`有未保存的更改`,clearOverride:`清除覆盖`,clear:`清除`,inheritingDefault:`正在继承 Desktop 默认值`,nothingToSave:`没有可保存的内容`,saved:`终端设置已保存`},d={heading:`代码板块`,summaryLanguage:`摘要语言`,monthlyBudget:`每月预算(USD)`,budgetHelper:"每个自然月 `surface=file-summary` 支出的上限。用户手动发起的重新生成可超出此限制。"},f={taglines:{dracula:`经典原版 — 紫色调暗色主题,霓虹色点缀鲜明`,"aurora-light":`高级浅色 — 受 Linear 启发的靛蓝配暖白底色`,"obsidian-dark":`高级深色 — 近黑蓝调配电光色点缀`,matrix:`磷光终端 — 绿黑底色上的柔和薄荷绿`,specrails:`品牌主题 — 深海军靛蓝配饱和青色点缀`},heading:`外观`,themeGroupLabel:`主题`,currentlyActive:`当前使用中`,updateFailed:`更新主题失败`},p={heading:`移动伴侣`,description:`通过手机上的 SpecRails Companion 应用控制 Specrails,完全经由本地网络。默认关闭。`,accessOn:`移动访问已开启`,accessOff:`移动访问已关闭`,listeningOnPort:`正在监听端口 {{port}}`,notListening:`未在监听`,turnOn:`开启`,turnOff:`关闭`,pairWebDevice:`配对网页伴侣`,reset:`重置`,resetConfirm:`确定要重置移动身份吗?所有已配对设备都将被吊销,必须重新配对。`,identityReset:`移动身份已重置`,enableFailed:`无法启用移动访问:{{message}}`,disableFailed:`无法禁用移动访问:{{message}}`,windowsFirewall:`首次启用时,Windows 防火墙会询问是否允许 SpecRails 服务器——请选择“允许在专用网络上”。`,pairedDevices:`已配对设备`,noDevices:`尚无已配对设备。`,revokeDevice:`吊销 {{name}}`},m={title:`配对网页伴侣`,description:`在手机上打开 specrails.dev/companion-app,然后用它扫描此二维码。`,startFailed:`无法开始配对:{{error}}`,showThenScan:`手机扫描此码后,点按“扫描手机的二维码”,并将摄像头对准手机。`,scanAnswer:`扫描手机的二维码`,scanning:`请将摄像头对准手机上的二维码…`,cameraFailed:`摄像头不可用:{{message}}`,notAnswer:`该二维码不是配对应答。`,connecting:`正在连接…`,paired:`✓ 已配对`,pairedToast:`网页伴侣已配对`,answerRejected:`桌面端拒绝了该码。请重新生成一个并重试。`,copyCode:`复制配对码`,codeCopied:`配对码已复制`,cancel:`取消`},h={language:e,page:t,errors:n,telemetry:r,prePrompt:i,ultraPrePrompt:a,budget:o,desktop:s,notifications:c,webhooks:l,terminal:u,codeSection:d,appearance:f,mobile:p,pairWeb:m};export{f as appearance,o as budget,d as codeSection,h as default,s as desktop,n as errors,e as language,p as mobile,c as notifications,t as page,m as pairWeb,i as prePrompt,r as telemetry,u as terminal,a as ultraPrePrompt,l as webhooks};
|
|
1
|
+
var e={title:`语言`,description:`Desktop 全局界面语言。即时生效,无需重启。`,selectLabel:`界面语言`,updateFailed:`无法保存语言偏好`},t={title:`项目设置`},n={saveFailed:`保存失败:{{message}}`},r={title:`流水线遥测`,description:`采集 token 用量、阶段耗时和子 Agent 活动以供诊断导出。默认关闭。`,toggleLabel:`启用流水线遥测`,toggleDescription:`开启后,流水线任务的 OTEL 数据将在本地采集。在任意任务卡片上使用<mono>导出诊断</mono>按钮即可下载。`,enabled:`已启用流水线遥测`,disabled:`已禁用流水线遥测`,saveFailed:`保存遥测设置失败`},i={title:`Rail 前置提示词`,description:`追加到 implement 与 batch-implement rail 任务中的项目专属附加指令,位于工单上下文之后、开始执行之前。`,label:`前置提示词`,placeholder:`示例:优先采用增量修改,保持迁移向后兼容,并为每次 rail 变更添加测试。`,helper:`用于应随每次 rail 实现运行附带的稳定项目指引。`,saveButton:`保存前置提示词`,cleared:`前置提示词已清除`,saved:`前置提示词已保存`,saveFailed:`保存前置提示词失败`},a={title:`Ultracode 前置提示词`,description:`在 Ultracode(仅限 Claude 的 rail)模式下发送给 Claude 的指令。Ultracode 跳过 OpenSpec 流水线——直接把该前置提示词加上 spec 文本交给 Claude,让其自主实现。留空则使用内置默认值。`,label:`Ultra 前置提示词`,placeholder:`留空以使用默认的 Ultracode 指令。`,helper:`spec 文本会自动追加在该前置提示词之后。留空 = 使用默认值。`,saveButton:`保存 Ultra 前置提示词`,resetToDefault:`Ultra 前置提示词已重置为默认值`,saved:`Ultra 前置提示词已保存`,saveFailed:`保存 Ultra 前置提示词失败`},o={title:`预算`,description:`为此项目设置每日支出上限。达到上限时队列自动暂停。`,dailyLabel:`每日预算(USD)`,dailyHelper:`留空表示禁用。支出按最近 24 小时计算。`,dailyPlaceholder:`如 5.00`,perJobLabel:`单任务成本告警(USD)`,perJobHelper:`当此项目中的单个任务超过此金额时告警。`,perJobPlaceholder:`如 0.50`,invalidNumber:`请输入正数,或留空以禁用`,dailyRemoved:`已移除每日预算`,dailySet:"每日预算已设为 ${{amount}}",perJobAlertDisabled:`已禁用单任务成本告警`,alertSet:"已设置任务超过 ${{amount}} 时告警",saveBudgetFailed:`保存预算失败`,saveThresholdFailed:`保存阈值失败`},s={title:`Desktop 设置`,description:`管理已注册的项目并查看 Desktop 信息。`,registeredProjects:`已注册项目`,noProjects:`尚未注册任何项目`,techUrlDescription:`specrails-tech API 的基础 URL(默认:http://localhost:3000)`,techUrlSaved:`specrails-tech URL 已保存`,techUrlSaveFailed:`保存 URL 失败`,budgetAlertsHeading:`预算与告警`,dailyBudgetLabel:`Desktop 每日预算(USD)`,dailyBudgetHelper:`所有项目的全局每日支出上限。超出后队列自动暂停。`,dailyBudgetPlaceholder:`如 10.00`,perJobHelper:`当单个任务超过此金额时告警。留空表示禁用。`,costAlertsDisabled:`已禁用成本告警`,dailyBudgetRemoved:`已移除 Desktop 每日预算`,dailyBudgetSet:"Desktop 每日预算已设为 ${{amount}}",dailyBudgetSaveFailed:`保存 Desktop 每日预算失败`,projectRemoved:`项目已移除`,projectRemoveFailed:`移除项目失败`,onboardingHeading:`新手引导`,platformTour:`平台导览`,platformTourDescription:`重播欢迎向导,回顾核心功能。`,replayTour:`重播导览`,terminalPanelHeading:`终端面板`,infoHeading:`Desktop 信息`,infoPort:`端口`,infoProjects:`项目`,infoDb:`Desktop 数据库`},c={heading:`系统通知`,description:`任务完成或失败时显示原生桌面通知。仅在标签页未聚焦时显示通知。`,enableLabel:`启用系统通知`,notifyOn:`通知时机:`,filterAll:`全部(完成与失败)`,filterCompleted:`仅完成`,filterFailed:`仅失败`,enabledToast:`已启用系统通知`,disabledToast:`已禁用系统通知`},l={heading:`出站 Webhook`,description:`在 Desktop 事件发生时通知外部工具(Slack、Zapier、CI/CD)。设置密钥后,请求会通过 <code>X-Specrails-Signature</code> 进行签名。`,eventJobCompleted:`任务完成`,eventJobFailed:`任务失败`,eventDailyBudgetExceeded:`超出每日预算`,statusOn:`开`,statusOff:`关`,disable:`禁用`,enable:`启用`,sendTestPing:`发送测试 ping`,addHeading:`添加 Webhook`,secretPlaceholder:`签名密钥(可选)`,addButton:`添加 Webhook`,urlRequired:`URL 为必填项`,selectEvent:`请至少选择一个事件`,added:`Webhook 已添加`,addFailed:`添加 Webhook 失败`,updateFailed:`更新 Webhook 失败`,removed:`Webhook 已移除`,removeFailed:`移除 Webhook 失败`,testPingSent:`测试 ping 已发送`,testPingFailed:`发送测试 ping 失败`},u={title:`终端面板`,desktopDescription:`Desktop 全局默认值,应用于所有项目,除非设置了项目级覆盖。`,projectDescription:`终端面板的项目级覆盖。保持字段不变即继承 Desktop 默认值。`,fontFamily:`字体`,fontSize:`字号({{min}}–{{max}})`,renderMode:`渲染模式`,copyOnSelect:`选中即复制`,shellIntegration:`Shell 集成(OSC 133 标记)`,notifyLongRunning:`长耗时命令通知`,longCommandThreshold:`长命令阈值(ms)`,imageRendering:`内联图片渲染(Sixel + iTerm2)`,browserShortcutUrl:`浏览器快捷方式 URL`,quickScript:`快捷脚本(粘贴到活动终端——需手动按 Enter)`,reset:`重置`,unsavedChanges:`有未保存的更改`,clearOverride:`清除覆盖`,clear:`清除`,inheritingDefault:`正在继承 Desktop 默认值`,nothingToSave:`没有可保存的内容`,saved:`终端设置已保存`},d={heading:`代码板块`,summaryLanguage:`摘要语言`,monthlyBudget:`每月预算(USD)`,budgetHelper:"每个自然月 `surface=file-summary` 支出的上限。用户手动发起的重新生成可超出此限制。"},f={taglines:{dracula:`经典原版 — 紫色调暗色主题,霓虹色点缀鲜明`,"aurora-light":`高级浅色 — 受 Linear 启发的靛蓝配暖白底色`,"obsidian-dark":`高级深色 — 近黑蓝调配电光色点缀`,matrix:`磷光终端 — 绿黑底色上的柔和薄荷绿`,specrails:`品牌主题 — 深海军靛蓝配饱和青色点缀`},heading:`外观`,themeGroupLabel:`主题`,currentlyActive:`当前使用中`,updateFailed:`更新主题失败`},p={heading:`移动伴侣`,description:`通过手机上的 SpecRails Companion 应用控制 Specrails,完全经由本地网络。默认关闭。`,accessOn:`移动访问已开启`,accessOff:`移动访问已关闭`,listeningOnPort:`正在监听端口 {{port}}`,notListening:`未在监听`,turnOn:`开启`,turnOff:`关闭`,pairWebDevice:`配对网页伴侣`,reset:`重置`,resetConfirm:`确定要重置移动身份吗?所有已配对设备都将被吊销,必须重新配对。`,identityReset:`移动身份已重置`,enableFailed:`无法启用移动访问:{{message}}`,disableFailed:`无法禁用移动访问:{{message}}`,windowsFirewall:`首次启用时,Windows 防火墙会询问是否允许 SpecRails 服务器——请选择“允许在专用网络上”。`,pairedDevices:`已配对设备`,noDevices:`尚无已配对设备。`,revokeDevice:`吊销 {{name}}`},m={title:`配对网页伴侣`,description:`在手机上打开 specrails.dev/companion-app,然后用它扫描此二维码。`,startFailed:`无法开始配对:{{error}}`,showThenScan:`手机扫描此码后,点按“扫描手机的二维码”,并将摄像头对准手机。`,scanAnswer:`扫描手机的二维码`,scanning:`请将摄像头对准手机上的二维码…`,cameraFailed:`摄像头不可用:{{message}}`,notAnswer:`该二维码不是配对应答。`,connecting:`正在连接…`,paired:`✓ 已配对`,pairedToast:`网页伴侣已配对`,answerRejected:`桌面端拒绝了该码。请重新生成一个并重试。`,copyCode:`复制配对码`,codeCopied:`配对码已复制`,cancel:`取消`},h={heading:`Specrails Core`,installed:`已安装:`,updateAvailable:`有可用更新:{{version}}`,upToDate:`已是最新`,neverChecked:`尚未检查`,check:`检查更新`,checking:`检查中…`,update:`更新到 {{version}}`,downloading:`下载中…`,materializing:`安装中…`,affectsAll:`适用于所有项目 — 无需重启。`,unavailable:`此版本不支持核心更新。`,toastDone:`Specrails Core 已更新到 {{version}}`,toastError:`核心更新失败:{{message}}`,toastUpToDate:`Specrails Core 已是最新`,toastCheckFailed:`检查更新失败:{{message}}`},g={language:e,page:t,errors:n,telemetry:r,prePrompt:i,ultraPrePrompt:a,budget:o,desktop:s,notifications:c,webhooks:l,terminal:u,codeSection:d,appearance:f,mobile:p,pairWeb:m,coreUpdate:h};export{f as appearance,o as budget,d as codeSection,h as coreUpdate,g as default,s as desktop,n as errors,e as language,p as mobile,c as notifications,t as page,m as pairWeb,i as prePrompt,r as telemetry,u as terminal,a as ultraPrePrompt,l as webhooks};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={critical:`Kritisch`,high:`Hoch`,medium:`Mittel`,low:`Niedrig`},t={todo:`To-do`,inProgress:`In Arbeit`,done:`Fertig`,cancelled:`Abgebrochen`},n={manual:`Manuell erstellt`,productBacklog:`Aus dem Product-Backlog`,proposeSpec:`Aus einem Spec-Vorschlag`,getBacklogSpecs:`Aus Backlog-Specs`,freePrompt:`Aus Raw-Prompt`},r={title:`Titel`,description:`Beschreibung`,status:`Status`,priority:`Priorität`,labels:`Labels`,markdownPlaceholder:`Markdown-Beschreibung…`,supportsMarkdown:`Unterstützt Markdown`},i={createdAgo:`Erstellt {{time}}`,updatedAgo:`Aktualisiert {{time}}`},a=`Unbenannt`,o={title:`Ticket löschen`,body:`Möchtest du <strong>#{{id}} {{title}}</strong> wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.`},s={toast:{updated:`Ticket aktualisiert`,updateFailed:`Ticket konnte nicht aktualisiert werden`},compare:`Vergleichen`,compareTooltip:`Mit einer anderen Spec vergleichen`,doneEditing:`Bearbeitung abschließen`,addDescription:`Beschreibung hinzufügen…`,prerequisites:`Voraussetzungen`,addLabel:`Label hinzufügen`,addLabelPlaceholder:`Label hinzufügen…`,effort:`Aufwand`,assignee:`Zugewiesen an`,continueEditing:`Weiter bearbeiten`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 befüllt`},l={moveToRail:`Auf Rail verschieben`,removeFromRail:`Von Rail entfernen`,currentlyOn:`Aktuell auf {{rail}}`,popoverTitle:`Auf Rail verschieben`,noRails:`Keine Rails verfügbar.`},u={ariaLabel:`Ticket-Aktionen`,deleteTicket:`Ticket löschen`,changeStatus:`Status ändern`,statusOptions:`Statusoptionen`,setPriority:`Priorität festlegen`,priorityOptions:`Prioritätsoptionen`},d={list:`Listenansicht`,grid:`Rasteransicht (Kanban)`,postit:`Post-it-Ansicht`},f={title:`Ticket erstellen`,titlePlaceholder:`Ticket-Titel…`,labelsPlaceholder:`Tippen, um Labels hinzuzufügen…`,create:`Erstellen`,creating:`Wird erstellt…`,toast:{created:`Ticket erstellt`,createFailed:`Ticket konnte nicht erstellt werden`}},p={title:`Vergleichen mit…`,
|
|
1
|
+
var e={critical:`Kritisch`,high:`Hoch`,medium:`Mittel`,low:`Niedrig`},t={todo:`To-do`,inProgress:`In Arbeit`,done:`Fertig`,cancelled:`Abgebrochen`},n={manual:`Manuell erstellt`,productBacklog:`Aus dem Product-Backlog`,proposeSpec:`Aus einem Spec-Vorschlag`,getBacklogSpecs:`Aus Backlog-Specs`,freePrompt:`Aus Raw-Prompt`},r={title:`Titel`,description:`Beschreibung`,status:`Status`,priority:`Priorität`,labels:`Labels`,markdownPlaceholder:`Markdown-Beschreibung…`,supportsMarkdown:`Unterstützt Markdown`},i={createdAgo:`Erstellt {{time}}`,updatedAgo:`Aktualisiert {{time}}`},a=`Unbenannt`,o={title:`Ticket löschen`,body:`Möchtest du <strong>#{{id}} {{title}}</strong> wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.`},s={toast:{updated:`Ticket aktualisiert`,updateFailed:`Ticket konnte nicht aktualisiert werden`},compare:`Vergleichen`,compareTooltip:`Mit einer anderen Spec vergleichen`,doneEditing:`Bearbeitung abschließen`,addDescription:`Beschreibung hinzufügen…`,prerequisites:`Voraussetzungen`,addLabel:`Label hinzufügen`,addLabelPlaceholder:`Label hinzufügen…`,effort:`Aufwand`,assignee:`Zugewiesen an`,continueEditing:`Weiter bearbeiten`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 befüllt`},l={moveToRail:`Auf Rail verschieben`,removeFromRail:`Von Rail entfernen`,currentlyOn:`Aktuell auf {{rail}}`,popoverTitle:`Auf Rail verschieben`,noRails:`Keine Rails verfügbar.`},u={ariaLabel:`Ticket-Aktionen`,deleteTicket:`Ticket löschen`,changeStatus:`Status ändern`,statusOptions:`Statusoptionen`,setPriority:`Priorität festlegen`,priorityOptions:`Prioritätsoptionen`},d={list:`Listenansicht`,grid:`Rasteransicht (Kanban)`,postit:`Post-it-Ansicht`},f={title:`Ticket erstellen`,titlePlaceholder:`Ticket-Titel…`,labelsPlaceholder:`Tippen, um Labels hinzuzufügen…`,create:`Erstellen`,creating:`Wird erstellt…`,toast:{created:`Ticket erstellt`,createFailed:`Ticket konnte nicht erstellt werden`}},p={title:`Vergleichen mit…`,count:`({{n}} Specs)`,searchPlaceholder:`Specs durchsuchen…`,noMatches:`Keine passenden Specs`,empty:`Keine anderen Specs zum Vergleichen`},m={ariaLabel:`Ticket-Ausgaben in Analytics anzeigen`,turns_one:`{{count}} Turn`,turns_other:`{{count}} Turns`,active:`{{duration}} aktiv`,breakdownItem:`{{n}} {{label}}`},h={newTicket:`Neues Ticket: {{title}}`,newTicketsAdded_one:`{{count}} neues Ticket aus der Produkt-Discovery hinzugefügt`,newTicketsAdded_other:`{{count}} neue Tickets aus der Produkt-Discovery hinzugefügt`},g={priority:e,ticketStatus:t,source:n,fields:r,meta:i,untitled:a,deleteDialog:o,detailModal:s,contractLayer:c,rail:l,contextMenu:u,viewMode:d,createModal:f,comparePicker:p,spendingLine:m,toasts:h};export{p as comparePicker,u as contextMenu,c as contractLayer,f as createModal,g as default,o as deleteDialog,s as detailModal,r as fields,i as meta,e as priority,l as rail,n as source,m as spendingLine,t as ticketStatus,h as toasts,a as untitled,d as viewMode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={critical:`Critica`,high:`Alta`,medium:`Media`,low:`Bassa`},t={todo:`Da fare`,inProgress:`In corso`,done:`Completato`,cancelled:`Annullato`},n={manual:`Creato manualmente`,productBacklog:`Dal product backlog`,proposeSpec:`Da proposta di spec`,getBacklogSpecs:`Dalle spec del backlog`,freePrompt:`Da prompt Raw`},r={title:`Titolo`,description:`Descrizione`,status:`Stato`,priority:`Priorità`,labels:`Etichette`,markdownPlaceholder:`Descrizione in Markdown...`,supportsMarkdown:`Supporta Markdown`},i={createdAgo:`Creato {{time}}`,updatedAgo:`Aggiornato {{time}}`},a=`Senza titolo`,o={title:`Elimina ticket`,body:`Vuoi davvero eliminare <strong>#{{id}} {{title}}</strong>? Questa azione è irreversibile.`},s={toast:{updated:`Ticket aggiornato`,updateFailed:`Impossibile aggiornare il ticket`},compare:`Confronta`,compareTooltip:`Confronta con un'altra spec`,doneEditing:`Fine modifica`,addDescription:`Aggiungi una descrizione...`,prerequisites:`Prerequisiti`,addLabel:`Aggiungi etichetta`,addLabelPlaceholder:`Aggiungi etichetta...`,effort:`Effort`,assignee:`Assegnatario`,continueEditing:`Continua a modificare`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 compilate`},l={moveToRail:`Sposta su un rail`,removeFromRail:`Rimuovi dal rail`,currentlyOn:`Attualmente su {{rail}}`,popoverTitle:`Sposta su un rail`,noRails:`Nessun rail disponibile.`},u={ariaLabel:`Azioni ticket`,deleteTicket:`Elimina ticket`,changeStatus:`Cambia stato`,statusOptions:`Opzioni di stato`,setPriority:`Imposta priorità`,priorityOptions:`Opzioni di priorità`},d={list:`Vista elenco`,grid:`Vista griglia (Kanban)`,postit:`Vista post-it`},f={title:`Crea ticket`,titlePlaceholder:`Titolo del ticket...`,labelsPlaceholder:`Digita per aggiungere etichette...`,create:`Crea`,creating:`Creazione...`,toast:{created:`Ticket creato`,createFailed:`Impossibile creare il ticket`}},p={title:`Confronta con…`,
|
|
1
|
+
var e={critical:`Critica`,high:`Alta`,medium:`Media`,low:`Bassa`},t={todo:`Da fare`,inProgress:`In corso`,done:`Completato`,cancelled:`Annullato`},n={manual:`Creato manualmente`,productBacklog:`Dal product backlog`,proposeSpec:`Da proposta di spec`,getBacklogSpecs:`Dalle spec del backlog`,freePrompt:`Da prompt Raw`},r={title:`Titolo`,description:`Descrizione`,status:`Stato`,priority:`Priorità`,labels:`Etichette`,markdownPlaceholder:`Descrizione in Markdown...`,supportsMarkdown:`Supporta Markdown`},i={createdAgo:`Creato {{time}}`,updatedAgo:`Aggiornato {{time}}`},a=`Senza titolo`,o={title:`Elimina ticket`,body:`Vuoi davvero eliminare <strong>#{{id}} {{title}}</strong>? Questa azione è irreversibile.`},s={toast:{updated:`Ticket aggiornato`,updateFailed:`Impossibile aggiornare il ticket`},compare:`Confronta`,compareTooltip:`Confronta con un'altra spec`,doneEditing:`Fine modifica`,addDescription:`Aggiungi una descrizione...`,prerequisites:`Prerequisiti`,addLabel:`Aggiungi etichetta`,addLabelPlaceholder:`Aggiungi etichetta...`,effort:`Effort`,assignee:`Assegnatario`,continueEditing:`Continua a modificare`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 compilate`},l={moveToRail:`Sposta su un rail`,removeFromRail:`Rimuovi dal rail`,currentlyOn:`Attualmente su {{rail}}`,popoverTitle:`Sposta su un rail`,noRails:`Nessun rail disponibile.`},u={ariaLabel:`Azioni ticket`,deleteTicket:`Elimina ticket`,changeStatus:`Cambia stato`,statusOptions:`Opzioni di stato`,setPriority:`Imposta priorità`,priorityOptions:`Opzioni di priorità`},d={list:`Vista elenco`,grid:`Vista griglia (Kanban)`,postit:`Vista post-it`},f={title:`Crea ticket`,titlePlaceholder:`Titolo del ticket...`,labelsPlaceholder:`Digita per aggiungere etichette...`,create:`Crea`,creating:`Creazione...`,toast:{created:`Ticket creato`,createFailed:`Impossibile creare il ticket`}},p={title:`Confronta con…`,count:`({{n}} spec)`,searchPlaceholder:`Cerca tra le spec…`,noMatches:`Nessuna spec corrispondente`,empty:`Nessun'altra spec da confrontare`},m={ariaLabel:`Vedi la spesa del ticket in Analytics`,turns_one:`{{count}} turno`,turns_other:`{{count}} turni`,active:`{{duration}} di attività`,breakdownItem:`{{n}} {{label}}`},h={newTicket:`Nuovo ticket: {{title}}`,newTicketsAdded_one:`{{count}} nuovo ticket aggiunto dalla product discovery`,newTicketsAdded_other:`{{count}} nuovi ticket aggiunti dalla product discovery`},g={priority:e,ticketStatus:t,source:n,fields:r,meta:i,untitled:a,deleteDialog:o,detailModal:s,contractLayer:c,rail:l,contextMenu:u,viewMode:d,createModal:f,comparePicker:p,spendingLine:m,toasts:h};export{p as comparePicker,u as contextMenu,c as contractLayer,f as createModal,g as default,o as deleteDialog,s as detailModal,r as fields,i as meta,e as priority,l as rail,n as source,m as spendingLine,t as ticketStatus,h as toasts,a as untitled,d as viewMode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./chunk-CilyBKbf.js";var t=e({comparePicker:()=>h,contextMenu:()=>f,contractLayer:()=>u,createModal:()=>m,default:()=>v,deleteDialog:()=>c,detailModal:()=>l,fields:()=>a,meta:()=>o,priority:()=>n,rail:()=>d,source:()=>i,spendingLine:()=>g,ticketStatus:()=>r,toasts:()=>_,untitled:()=>s,viewMode:()=>p}),n={critical:`Critical`,high:`High`,medium:`Medium`,low:`Low`},r={todo:`Todo`,inProgress:`In Progress`,done:`Done`,cancelled:`Cancelled`},i={manual:`Created manually`,productBacklog:`From product backlog`,proposeSpec:`From spec proposal`,getBacklogSpecs:`From backlog specs`,freePrompt:`From Raw prompt`},a={title:`Title`,description:`Description`,status:`Status`,priority:`Priority`,labels:`Labels`,markdownPlaceholder:`Markdown description...`,supportsMarkdown:`Supports markdown`},o={createdAgo:`Created {{time}}`,updatedAgo:`Updated {{time}}`},s=`Untitled`,c={title:`Delete ticket`,body:`Are you sure you want to delete <strong>#{{id}} {{title}}</strong>? This action cannot be undone.`},l={toast:{updated:`Ticket updated`,updateFailed:`Failed to update ticket`},compare:`Compare`,compareTooltip:`Compare with another spec`,doneEditing:`Done editing`,addDescription:`Add a description...`,prerequisites:`Prerequisites`,addLabel:`Add label`,addLabelPlaceholder:`Add label...`,effort:`Effort`,assignee:`Assignee`,continueEditing:`Continue Editing`,ticketFallbackLabel:`Ticket #{{id}}`},u={title:`Contract Layer`,populated:`{{n}}/5 populated`},d={moveToRail:`Move to Rail`,removeFromRail:`Remove from Rail`,currentlyOn:`Currently on {{rail}}`,popoverTitle:`Move to rail`,noRails:`No rails available.`},f={ariaLabel:`Ticket actions`,deleteTicket:`Delete ticket`,changeStatus:`Change status`,statusOptions:`Status options`,setPriority:`Set priority`,priorityOptions:`Priority options`},p={list:`List view`,grid:`Grid view (Kanban)`,postit:`Post-it view`},m={title:`Create Ticket`,titlePlaceholder:`Ticket title...`,labelsPlaceholder:`Type to add labels...`,create:`Create`,creating:`Creating...`,toast:{created:`Ticket created`,createFailed:`Failed to create ticket`}},h={title:`Compare with…`,
|
|
1
|
+
import{n as e}from"./chunk-CilyBKbf.js";var t=e({comparePicker:()=>h,contextMenu:()=>f,contractLayer:()=>u,createModal:()=>m,default:()=>v,deleteDialog:()=>c,detailModal:()=>l,fields:()=>a,meta:()=>o,priority:()=>n,rail:()=>d,source:()=>i,spendingLine:()=>g,ticketStatus:()=>r,toasts:()=>_,untitled:()=>s,viewMode:()=>p}),n={critical:`Critical`,high:`High`,medium:`Medium`,low:`Low`},r={todo:`Todo`,inProgress:`In Progress`,done:`Done`,cancelled:`Cancelled`},i={manual:`Created manually`,productBacklog:`From product backlog`,proposeSpec:`From spec proposal`,getBacklogSpecs:`From backlog specs`,freePrompt:`From Raw prompt`},a={title:`Title`,description:`Description`,status:`Status`,priority:`Priority`,labels:`Labels`,markdownPlaceholder:`Markdown description...`,supportsMarkdown:`Supports markdown`},o={createdAgo:`Created {{time}}`,updatedAgo:`Updated {{time}}`},s=`Untitled`,c={title:`Delete ticket`,body:`Are you sure you want to delete <strong>#{{id}} {{title}}</strong>? This action cannot be undone.`},l={toast:{updated:`Ticket updated`,updateFailed:`Failed to update ticket`},compare:`Compare`,compareTooltip:`Compare with another spec`,doneEditing:`Done editing`,addDescription:`Add a description...`,prerequisites:`Prerequisites`,addLabel:`Add label`,addLabelPlaceholder:`Add label...`,effort:`Effort`,assignee:`Assignee`,continueEditing:`Continue Editing`,ticketFallbackLabel:`Ticket #{{id}}`},u={title:`Contract Layer`,populated:`{{n}}/5 populated`},d={moveToRail:`Move to Rail`,removeFromRail:`Remove from Rail`,currentlyOn:`Currently on {{rail}}`,popoverTitle:`Move to rail`,noRails:`No rails available.`},f={ariaLabel:`Ticket actions`,deleteTicket:`Delete ticket`,changeStatus:`Change status`,statusOptions:`Status options`,setPriority:`Set priority`,priorityOptions:`Priority options`},p={list:`List view`,grid:`Grid view (Kanban)`,postit:`Post-it view`},m={title:`Create Ticket`,titlePlaceholder:`Ticket title...`,labelsPlaceholder:`Type to add labels...`,create:`Create`,creating:`Creating...`,toast:{created:`Ticket created`,createFailed:`Failed to create ticket`}},h={title:`Compare with…`,count:`({{n}} specs)`,searchPlaceholder:`Search specs…`,noMatches:`No matching specs`,empty:`No other specs to compare`},g={ariaLabel:`View ticket spending in Analytics`,turns_one:`{{count}} turn`,turns_other:`{{count}} turns`,active:`{{duration}} active`,breakdownItem:`{{n}} {{label}}`},_={newTicket:`New ticket: {{title}}`,newTicketsAdded_one:`{{count}} new ticket added from product discovery`,newTicketsAdded_other:`{{count}} new tickets added from product discovery`},v={priority:n,ticketStatus:r,source:i,fields:a,meta:o,untitled:s,deleteDialog:c,detailModal:l,contractLayer:u,rail:d,contextMenu:f,viewMode:p,createModal:m,comparePicker:h,spendingLine:g,toasts:_};export{t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={critical:`クリティカル`,high:`高`,medium:`中`,low:`低`},t={todo:`Todo`,inProgress:`進行中`,done:`完了`,cancelled:`キャンセル`},n={manual:`手動で作成`,productBacklog:`プロダクトバックログから`,proposeSpec:`スペック提案から`,getBacklogSpecs:`バックログスペックから`,freePrompt:`Raw プロンプトから`},r={title:`タイトル`,description:`説明`,status:`ステータス`,priority:`優先度`,labels:`ラベル`,markdownPlaceholder:`Markdown で説明...`,supportsMarkdown:`Markdown 対応`},i={createdAgo:`{{time}}に作成`,updatedAgo:`{{time}}に更新`},a=`無題`,o={title:`チケットを削除`,body:`<strong>#{{id}} {{title}}</strong> を削除してもよろしいですか?この操作は元に戻せません。`},s={toast:{updated:`チケットを更新しました`,updateFailed:`チケットの更新に失敗しました`},compare:`比較`,compareTooltip:`別のスペックと比較`,doneEditing:`編集を終了`,addDescription:`説明を追加...`,prerequisites:`前提条件`,addLabel:`ラベルを追加`,addLabelPlaceholder:`ラベルを追加...`,effort:`工数`,assignee:`担当者`,continueEditing:`編集を続ける`,ticketFallbackLabel:`チケット #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 件入力済み`},l={moveToRail:`レールに移動`,removeFromRail:`レールから削除`,currentlyOn:`現在 {{rail}} 上にあります`,popoverTitle:`レールに移動`,noRails:`利用可能なレールがありません。`},u={ariaLabel:`チケット操作`,deleteTicket:`チケットを削除`,changeStatus:`ステータスを変更`,statusOptions:`ステータスオプション`,setPriority:`優先度を設定`,priorityOptions:`優先度オプション`},d={list:`リスト表示`,grid:`グリッド表示(カンバン)`,postit:`付箋表示`},f={title:`チケットを作成`,titlePlaceholder:`チケットのタイトル...`,labelsPlaceholder:`入力してラベルを追加...`,create:`作成`,creating:`作成中...`,toast:{created:`チケットを作成しました`,createFailed:`チケットの作成に失敗しました`}},p={title:`比較対象を選択…`,
|
|
1
|
+
var e={critical:`クリティカル`,high:`高`,medium:`中`,low:`低`},t={todo:`Todo`,inProgress:`進行中`,done:`完了`,cancelled:`キャンセル`},n={manual:`手動で作成`,productBacklog:`プロダクトバックログから`,proposeSpec:`スペック提案から`,getBacklogSpecs:`バックログスペックから`,freePrompt:`Raw プロンプトから`},r={title:`タイトル`,description:`説明`,status:`ステータス`,priority:`優先度`,labels:`ラベル`,markdownPlaceholder:`Markdown で説明...`,supportsMarkdown:`Markdown 対応`},i={createdAgo:`{{time}}に作成`,updatedAgo:`{{time}}に更新`},a=`無題`,o={title:`チケットを削除`,body:`<strong>#{{id}} {{title}}</strong> を削除してもよろしいですか?この操作は元に戻せません。`},s={toast:{updated:`チケットを更新しました`,updateFailed:`チケットの更新に失敗しました`},compare:`比較`,compareTooltip:`別のスペックと比較`,doneEditing:`編集を終了`,addDescription:`説明を追加...`,prerequisites:`前提条件`,addLabel:`ラベルを追加`,addLabelPlaceholder:`ラベルを追加...`,effort:`工数`,assignee:`担当者`,continueEditing:`編集を続ける`,ticketFallbackLabel:`チケット #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 件入力済み`},l={moveToRail:`レールに移動`,removeFromRail:`レールから削除`,currentlyOn:`現在 {{rail}} 上にあります`,popoverTitle:`レールに移動`,noRails:`利用可能なレールがありません。`},u={ariaLabel:`チケット操作`,deleteTicket:`チケットを削除`,changeStatus:`ステータスを変更`,statusOptions:`ステータスオプション`,setPriority:`優先度を設定`,priorityOptions:`優先度オプション`},d={list:`リスト表示`,grid:`グリッド表示(カンバン)`,postit:`付箋表示`},f={title:`チケットを作成`,titlePlaceholder:`チケットのタイトル...`,labelsPlaceholder:`入力してラベルを追加...`,create:`作成`,creating:`作成中...`,toast:{created:`チケットを作成しました`,createFailed:`チケットの作成に失敗しました`}},p={title:`比較対象を選択…`,count:`(スペック {{n}} 件)`,searchPlaceholder:`スペックを検索…`,noMatches:`一致するスペックはありません`,empty:`比較できる他のスペックはありません`},m={ariaLabel:`アナリティクスでチケットの支出を表示`,turns_one:`{{count}} ターン`,turns_other:`{{count}} ターン`,active:`アクティブ {{duration}}`,breakdownItem:`{{n}} {{label}}`},h={newTicket:`新しいチケット: {{title}}`,newTicketsAdded_one:`プロダクトディスカバリーから {{count}} 件の新しいチケットを追加しました`,newTicketsAdded_other:`プロダクトディスカバリーから {{count}} 件の新しいチケットを追加しました`},g={priority:e,ticketStatus:t,source:n,fields:r,meta:i,untitled:a,deleteDialog:o,detailModal:s,contractLayer:c,rail:l,contextMenu:u,viewMode:d,createModal:f,comparePicker:p,spendingLine:m,toasts:h};export{p as comparePicker,u as contextMenu,c as contractLayer,f as createModal,g as default,o as deleteDialog,s as detailModal,r as fields,i as meta,e as priority,l as rail,n as source,m as spendingLine,t as ticketStatus,h as toasts,a as untitled,d as viewMode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={critical:`Crítica`,high:`Alta`,medium:`Média`,low:`Baixa`},t={todo:`Por fazer`,inProgress:`Em curso`,done:`Concluído`,cancelled:`Cancelado`},n={manual:`Criado manualmente`,productBacklog:`Do product backlog`,proposeSpec:`De proposta de spec`,getBacklogSpecs:`De specs do backlog`,freePrompt:`De prompt Raw`},r={title:`Título`,description:`Descrição`,status:`Estado`,priority:`Prioridade`,labels:`Etiquetas`,markdownPlaceholder:`Descrição em Markdown...`,supportsMarkdown:`Suporta Markdown`},i={createdAgo:`Criado {{time}}`,updatedAgo:`Atualizado {{time}}`},a=`Sem título`,o={title:`Eliminar ticket`,body:`Tem a certeza de que quer eliminar <strong>#{{id}} {{title}}</strong>? Esta ação não pode ser anulada.`},s={toast:{updated:`Ticket atualizado`,updateFailed:`Falha ao atualizar o ticket`},compare:`Comparar`,compareTooltip:`Comparar com outra spec`,doneEditing:`Terminar edição`,addDescription:`Adicionar uma descrição...`,prerequisites:`Pré-requisitos`,addLabel:`Adicionar etiqueta`,addLabelPlaceholder:`Adicionar etiqueta...`,effort:`Esforço`,assignee:`Responsável`,continueEditing:`Continuar a editar`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 preenchidas`},l={moveToRail:`Mover para um rail`,removeFromRail:`Remover do rail`,currentlyOn:`Atualmente em {{rail}}`,popoverTitle:`Mover para um rail`,noRails:`Nenhum rail disponível.`},u={ariaLabel:`Ações do ticket`,deleteTicket:`Eliminar ticket`,changeStatus:`Alterar estado`,statusOptions:`Opções de estado`,setPriority:`Definir prioridade`,priorityOptions:`Opções de prioridade`},d={list:`Vista de lista`,grid:`Vista de grelha (Kanban)`,postit:`Vista de post-its`},f={title:`Criar ticket`,titlePlaceholder:`Título do ticket...`,labelsPlaceholder:`Escreva para adicionar etiquetas...`,create:`Criar`,creating:`A criar...`,toast:{created:`Ticket criado`,createFailed:`Falha ao criar o ticket`}},p={title:`Comparar com…`,
|
|
1
|
+
var e={critical:`Crítica`,high:`Alta`,medium:`Média`,low:`Baixa`},t={todo:`Por fazer`,inProgress:`Em curso`,done:`Concluído`,cancelled:`Cancelado`},n={manual:`Criado manualmente`,productBacklog:`Do product backlog`,proposeSpec:`De proposta de spec`,getBacklogSpecs:`De specs do backlog`,freePrompt:`De prompt Raw`},r={title:`Título`,description:`Descrição`,status:`Estado`,priority:`Prioridade`,labels:`Etiquetas`,markdownPlaceholder:`Descrição em Markdown...`,supportsMarkdown:`Suporta Markdown`},i={createdAgo:`Criado {{time}}`,updatedAgo:`Atualizado {{time}}`},a=`Sem título`,o={title:`Eliminar ticket`,body:`Tem a certeza de que quer eliminar <strong>#{{id}} {{title}}</strong>? Esta ação não pode ser anulada.`},s={toast:{updated:`Ticket atualizado`,updateFailed:`Falha ao atualizar o ticket`},compare:`Comparar`,compareTooltip:`Comparar com outra spec`,doneEditing:`Terminar edição`,addDescription:`Adicionar uma descrição...`,prerequisites:`Pré-requisitos`,addLabel:`Adicionar etiqueta`,addLabelPlaceholder:`Adicionar etiqueta...`,effort:`Esforço`,assignee:`Responsável`,continueEditing:`Continuar a editar`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 preenchidas`},l={moveToRail:`Mover para um rail`,removeFromRail:`Remover do rail`,currentlyOn:`Atualmente em {{rail}}`,popoverTitle:`Mover para um rail`,noRails:`Nenhum rail disponível.`},u={ariaLabel:`Ações do ticket`,deleteTicket:`Eliminar ticket`,changeStatus:`Alterar estado`,statusOptions:`Opções de estado`,setPriority:`Definir prioridade`,priorityOptions:`Opções de prioridade`},d={list:`Vista de lista`,grid:`Vista de grelha (Kanban)`,postit:`Vista de post-its`},f={title:`Criar ticket`,titlePlaceholder:`Título do ticket...`,labelsPlaceholder:`Escreva para adicionar etiquetas...`,create:`Criar`,creating:`A criar...`,toast:{created:`Ticket criado`,createFailed:`Falha ao criar o ticket`}},p={title:`Comparar com…`,count:`({{n}} specs)`,searchPlaceholder:`Pesquisar specs…`,noMatches:`Nenhuma spec corresponde`,empty:`Não há outras specs para comparar`},m={ariaLabel:`Ver os gastos do ticket em Analytics`,turns_one:`{{count}} turno`,turns_other:`{{count}} turnos`,active:`{{duration}} ativo`,breakdownItem:`{{n}} {{label}}`},h={newTicket:`Novo ticket: {{title}}`,newTicketsAdded_one:`{{count}} novo ticket adicionado a partir da descoberta de produto`,newTicketsAdded_other:`{{count}} novos tickets adicionados a partir da descoberta de produto`},g={priority:e,ticketStatus:t,source:n,fields:r,meta:i,untitled:a,deleteDialog:o,detailModal:s,contractLayer:c,rail:l,contextMenu:u,viewMode:d,createModal:f,comparePicker:p,spendingLine:m,toasts:h};export{p as comparePicker,u as contextMenu,c as contractLayer,f as createModal,g as default,o as deleteDialog,s as detailModal,r as fields,i as meta,e as priority,l as rail,n as source,m as spendingLine,t as ticketStatus,h as toasts,a as untitled,d as viewMode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={critical:`紧急`,high:`高`,medium:`中`,low:`低`},t={todo:`待办`,inProgress:`进行中`,done:`完成`,cancelled:`已取消`},n={manual:`手动创建`,productBacklog:`来自产品 backlog`,proposeSpec:`来自 spec 提案`,getBacklogSpecs:`来自 backlog spec`,freePrompt:`来自 Raw prompt`},r={title:`标题`,description:`描述`,status:`状态`,priority:`优先级`,labels:`标签`,markdownPlaceholder:`Markdown 描述…`,supportsMarkdown:`支持 Markdown`},i={createdAgo:`创建于 {{time}}`,updatedAgo:`更新于 {{time}}`},a=`未命名`,o={title:`删除工单`,body:`确定要删除 <strong>#{{id}} {{title}}</strong> 吗?此操作无法撤销。`},s={toast:{updated:`工单已更新`,updateFailed:`更新工单失败`},compare:`对比`,compareTooltip:`与另一个 spec 对比`,doneEditing:`完成编辑`,addDescription:`添加描述…`,prerequisites:`前置条件`,addLabel:`添加标签`,addLabelPlaceholder:`添加标签…`,effort:`工作量`,assignee:`负责人`,continueEditing:`继续编辑`,ticketFallbackLabel:`工单 #{{id}}`},c={title:`Contract Layer`,populated:`已填充 {{n}}/5`},l={moveToRail:`放入 Rail`,removeFromRail:`从 Rail 移除`,currentlyOn:`当前位于 {{rail}}`,popoverTitle:`放入 rail`,noRails:`没有可用的 rail。`},u={ariaLabel:`工单操作`,deleteTicket:`删除工单`,changeStatus:`更改状态`,statusOptions:`状态选项`,setPriority:`设置优先级`,priorityOptions:`优先级选项`},d={list:`列表视图`,grid:`网格视图(看板)`,postit:`便签视图`},f={title:`创建工单`,titlePlaceholder:`工单标题…`,labelsPlaceholder:`输入以添加标签…`,create:`创建`,creating:`正在创建…`,toast:{created:`工单已创建`,createFailed:`创建工单失败`}},p={title:`选择对比对象…`,
|
|
1
|
+
var e={critical:`紧急`,high:`高`,medium:`中`,low:`低`},t={todo:`待办`,inProgress:`进行中`,done:`完成`,cancelled:`已取消`},n={manual:`手动创建`,productBacklog:`来自产品 backlog`,proposeSpec:`来自 spec 提案`,getBacklogSpecs:`来自 backlog spec`,freePrompt:`来自 Raw prompt`},r={title:`标题`,description:`描述`,status:`状态`,priority:`优先级`,labels:`标签`,markdownPlaceholder:`Markdown 描述…`,supportsMarkdown:`支持 Markdown`},i={createdAgo:`创建于 {{time}}`,updatedAgo:`更新于 {{time}}`},a=`未命名`,o={title:`删除工单`,body:`确定要删除 <strong>#{{id}} {{title}}</strong> 吗?此操作无法撤销。`},s={toast:{updated:`工单已更新`,updateFailed:`更新工单失败`},compare:`对比`,compareTooltip:`与另一个 spec 对比`,doneEditing:`完成编辑`,addDescription:`添加描述…`,prerequisites:`前置条件`,addLabel:`添加标签`,addLabelPlaceholder:`添加标签…`,effort:`工作量`,assignee:`负责人`,continueEditing:`继续编辑`,ticketFallbackLabel:`工单 #{{id}}`},c={title:`Contract Layer`,populated:`已填充 {{n}}/5`},l={moveToRail:`放入 Rail`,removeFromRail:`从 Rail 移除`,currentlyOn:`当前位于 {{rail}}`,popoverTitle:`放入 rail`,noRails:`没有可用的 rail。`},u={ariaLabel:`工单操作`,deleteTicket:`删除工单`,changeStatus:`更改状态`,statusOptions:`状态选项`,setPriority:`设置优先级`,priorityOptions:`优先级选项`},d={list:`列表视图`,grid:`网格视图(看板)`,postit:`便签视图`},f={title:`创建工单`,titlePlaceholder:`工单标题…`,labelsPlaceholder:`输入以添加标签…`,create:`创建`,creating:`正在创建…`,toast:{created:`工单已创建`,createFailed:`创建工单失败`}},p={title:`选择对比对象…`,count:`({{n}} 个 spec)`,searchPlaceholder:`搜索 spec…`,noMatches:`没有匹配的 spec`,empty:`没有其他可对比的 spec`},m={ariaLabel:`在分析页查看此工单的支出`,turns_one:`{{count}} 轮`,turns_other:`{{count}} 轮`,active:`活跃 {{duration}}`,breakdownItem:`{{n}} {{label}}`},h={newTicket:`新工单:{{title}}`,newTicketsAdded_one:`已从产品发现新增 {{count}} 个工单`,newTicketsAdded_other:`已从产品发现新增 {{count}} 个工单`},g={priority:e,ticketStatus:t,source:n,fields:r,meta:i,untitled:a,deleteDialog:o,detailModal:s,contractLayer:c,rail:l,contextMenu:u,viewMode:d,createModal:f,comparePicker:p,spendingLine:m,toasts:h};export{p as comparePicker,u as contextMenu,c as contractLayer,f as createModal,g as default,o as deleteDialog,s as detailModal,r as fields,i as meta,e as priority,l as rail,n as source,m as spendingLine,t as ticketStatus,h as toasts,a as untitled,d as viewMode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={critical:`Critique`,high:`Haute`,medium:`Moyenne`,low:`Basse`},t={todo:`À faire`,inProgress:`En cours`,done:`Terminé`,cancelled:`Annulé`},n={manual:`Créé manuellement`,productBacklog:`Depuis le product backlog`,proposeSpec:`Depuis une proposition de spec`,getBacklogSpecs:`Depuis les specs du backlog`,freePrompt:`Depuis un prompt Raw`},r={title:`Titre`,description:`Description`,status:`Statut`,priority:`Priorité`,labels:`Labels`,markdownPlaceholder:`Description en Markdown…`,supportsMarkdown:`Markdown pris en charge`},i={createdAgo:`Créé {{time}}`,updatedAgo:`Mis à jour {{time}}`},a=`Sans titre`,o={title:`Supprimer le ticket`,body:`Voulez-vous vraiment supprimer <strong>#{{id}} {{title}}</strong> ? Cette action est irréversible.`},s={toast:{updated:`Ticket mis à jour`,updateFailed:`Échec de la mise à jour du ticket`},compare:`Comparer`,compareTooltip:`Comparer avec une autre spec`,doneEditing:`Terminer l'édition`,addDescription:`Ajouter une description…`,prerequisites:`Prérequis`,addLabel:`Ajouter un label`,addLabelPlaceholder:`Ajouter un label…`,effort:`Effort`,assignee:`Assigné à`,continueEditing:`Continuer l'édition`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 renseignés`},l={moveToRail:`Déplacer vers un rail`,removeFromRail:`Retirer du rail`,currentlyOn:`Actuellement sur {{rail}}`,popoverTitle:`Déplacer vers un rail`,noRails:`Aucun rail disponible.`},u={ariaLabel:`Actions du ticket`,deleteTicket:`Supprimer le ticket`,changeStatus:`Changer le statut`,statusOptions:`Options de statut`,setPriority:`Définir la priorité`,priorityOptions:`Options de priorité`},d={list:`Vue liste`,grid:`Vue grille (Kanban)`,postit:`Vue post-it`},f={title:`Créer un ticket`,titlePlaceholder:`Titre du ticket…`,labelsPlaceholder:`Saisissez pour ajouter des labels…`,create:`Créer`,creating:`Création…`,toast:{created:`Ticket créé`,createFailed:`Échec de la création du ticket`}},p={title:`Comparer avec…`,
|
|
1
|
+
var e={critical:`Critique`,high:`Haute`,medium:`Moyenne`,low:`Basse`},t={todo:`À faire`,inProgress:`En cours`,done:`Terminé`,cancelled:`Annulé`},n={manual:`Créé manuellement`,productBacklog:`Depuis le product backlog`,proposeSpec:`Depuis une proposition de spec`,getBacklogSpecs:`Depuis les specs du backlog`,freePrompt:`Depuis un prompt Raw`},r={title:`Titre`,description:`Description`,status:`Statut`,priority:`Priorité`,labels:`Labels`,markdownPlaceholder:`Description en Markdown…`,supportsMarkdown:`Markdown pris en charge`},i={createdAgo:`Créé {{time}}`,updatedAgo:`Mis à jour {{time}}`},a=`Sans titre`,o={title:`Supprimer le ticket`,body:`Voulez-vous vraiment supprimer <strong>#{{id}} {{title}}</strong> ? Cette action est irréversible.`},s={toast:{updated:`Ticket mis à jour`,updateFailed:`Échec de la mise à jour du ticket`},compare:`Comparer`,compareTooltip:`Comparer avec une autre spec`,doneEditing:`Terminer l'édition`,addDescription:`Ajouter une description…`,prerequisites:`Prérequis`,addLabel:`Ajouter un label`,addLabelPlaceholder:`Ajouter un label…`,effort:`Effort`,assignee:`Assigné à`,continueEditing:`Continuer l'édition`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 renseignés`},l={moveToRail:`Déplacer vers un rail`,removeFromRail:`Retirer du rail`,currentlyOn:`Actuellement sur {{rail}}`,popoverTitle:`Déplacer vers un rail`,noRails:`Aucun rail disponible.`},u={ariaLabel:`Actions du ticket`,deleteTicket:`Supprimer le ticket`,changeStatus:`Changer le statut`,statusOptions:`Options de statut`,setPriority:`Définir la priorité`,priorityOptions:`Options de priorité`},d={list:`Vue liste`,grid:`Vue grille (Kanban)`,postit:`Vue post-it`},f={title:`Créer un ticket`,titlePlaceholder:`Titre du ticket…`,labelsPlaceholder:`Saisissez pour ajouter des labels…`,create:`Créer`,creating:`Création…`,toast:{created:`Ticket créé`,createFailed:`Échec de la création du ticket`}},p={title:`Comparer avec…`,count:`({{n}} specs)`,searchPlaceholder:`Rechercher des specs…`,noMatches:`Aucune spec correspondante`,empty:`Aucune autre spec à comparer`},m={ariaLabel:`Voir les dépenses du ticket dans Analytics`,turns_one:`{{count}} tour`,turns_other:`{{count}} tours`,active:`{{duration}} actif`,breakdownItem:`{{n}} {{label}}`},h={newTicket:`Nouveau ticket : {{title}}`,newTicketsAdded_one:`{{count}} nouveau ticket ajouté depuis la product discovery`,newTicketsAdded_other:`{{count}} nouveaux tickets ajoutés depuis la product discovery`},g={priority:e,ticketStatus:t,source:n,fields:r,meta:i,untitled:a,deleteDialog:o,detailModal:s,contractLayer:c,rail:l,contextMenu:u,viewMode:d,createModal:f,comparePicker:p,spendingLine:m,toasts:h};export{p as comparePicker,u as contextMenu,c as contractLayer,f as createModal,g as default,o as deleteDialog,s as detailModal,r as fields,i as meta,e as priority,l as rail,n as source,m as spendingLine,t as ticketStatus,h as toasts,a as untitled,d as viewMode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={critical:`Crítica`,high:`Alta`,medium:`Media`,low:`Baja`},t={todo:`Por hacer`,inProgress:`En curso`,done:`Hecho`,cancelled:`Cancelado`},n={manual:`Creado manualmente`,productBacklog:`Desde el product backlog`,proposeSpec:`Desde una propuesta de spec`,getBacklogSpecs:`Desde specs del backlog`,freePrompt:`Desde un prompt Raw`},r={title:`Título`,description:`Descripción`,status:`Estado`,priority:`Prioridad`,labels:`Etiquetas`,markdownPlaceholder:`Descripción en Markdown...`,supportsMarkdown:`Admite Markdown`},i={createdAgo:`Creado {{time}}`,updatedAgo:`Actualizado {{time}}`},a=`Sin título`,o={title:`Eliminar ticket`,body:`¿Seguro que quieres eliminar <strong>#{{id}} {{title}}</strong>? Esta acción no se puede deshacer.`},s={toast:{updated:`Ticket actualizado`,updateFailed:`No se pudo actualizar el ticket`},compare:`Comparar`,compareTooltip:`Comparar con otra spec`,doneEditing:`Terminar edición`,addDescription:`Añadir una descripción...`,prerequisites:`Requisitos previos`,addLabel:`Añadir etiqueta`,addLabelPlaceholder:`Añadir etiqueta...`,effort:`Esfuerzo`,assignee:`Asignado a`,continueEditing:`Seguir editando`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 con contenido`},l={moveToRail:`Mover a un rail`,removeFromRail:`Quitar del rail`,currentlyOn:`Actualmente en {{rail}}`,popoverTitle:`Mover a un rail`,noRails:`No hay rails disponibles.`},u={ariaLabel:`Acciones del ticket`,deleteTicket:`Eliminar ticket`,changeStatus:`Cambiar estado`,statusOptions:`Opciones de estado`,setPriority:`Establecer prioridad`,priorityOptions:`Opciones de prioridad`},d={list:`Vista de lista`,grid:`Vista de cuadrícula (Kanban)`,postit:`Vista post-it`},f={title:`Crear ticket`,titlePlaceholder:`Título del ticket...`,labelsPlaceholder:`Escribe para añadir etiquetas...`,create:`Crear`,creating:`Creando...`,toast:{created:`Ticket creado`,createFailed:`No se pudo crear el ticket`}},p={title:`Comparar con…`,
|
|
1
|
+
var e={critical:`Crítica`,high:`Alta`,medium:`Media`,low:`Baja`},t={todo:`Por hacer`,inProgress:`En curso`,done:`Hecho`,cancelled:`Cancelado`},n={manual:`Creado manualmente`,productBacklog:`Desde el product backlog`,proposeSpec:`Desde una propuesta de spec`,getBacklogSpecs:`Desde specs del backlog`,freePrompt:`Desde un prompt Raw`},r={title:`Título`,description:`Descripción`,status:`Estado`,priority:`Prioridad`,labels:`Etiquetas`,markdownPlaceholder:`Descripción en Markdown...`,supportsMarkdown:`Admite Markdown`},i={createdAgo:`Creado {{time}}`,updatedAgo:`Actualizado {{time}}`},a=`Sin título`,o={title:`Eliminar ticket`,body:`¿Seguro que quieres eliminar <strong>#{{id}} {{title}}</strong>? Esta acción no se puede deshacer.`},s={toast:{updated:`Ticket actualizado`,updateFailed:`No se pudo actualizar el ticket`},compare:`Comparar`,compareTooltip:`Comparar con otra spec`,doneEditing:`Terminar edición`,addDescription:`Añadir una descripción...`,prerequisites:`Requisitos previos`,addLabel:`Añadir etiqueta`,addLabelPlaceholder:`Añadir etiqueta...`,effort:`Esfuerzo`,assignee:`Asignado a`,continueEditing:`Seguir editando`,ticketFallbackLabel:`Ticket #{{id}}`},c={title:`Contract Layer`,populated:`{{n}}/5 con contenido`},l={moveToRail:`Mover a un rail`,removeFromRail:`Quitar del rail`,currentlyOn:`Actualmente en {{rail}}`,popoverTitle:`Mover a un rail`,noRails:`No hay rails disponibles.`},u={ariaLabel:`Acciones del ticket`,deleteTicket:`Eliminar ticket`,changeStatus:`Cambiar estado`,statusOptions:`Opciones de estado`,setPriority:`Establecer prioridad`,priorityOptions:`Opciones de prioridad`},d={list:`Vista de lista`,grid:`Vista de cuadrícula (Kanban)`,postit:`Vista post-it`},f={title:`Crear ticket`,titlePlaceholder:`Título del ticket...`,labelsPlaceholder:`Escribe para añadir etiquetas...`,create:`Crear`,creating:`Creando...`,toast:{created:`Ticket creado`,createFailed:`No se pudo crear el ticket`}},p={title:`Comparar con…`,count:`({{n}} specs)`,searchPlaceholder:`Buscar specs…`,noMatches:`Ninguna spec coincide`,empty:`No hay otras specs para comparar`},m={ariaLabel:`Ver el gasto del ticket en Analíticas`,turns_one:`{{count}} turno`,turns_other:`{{count}} turnos`,active:`{{duration}} activo`,breakdownItem:`{{n}} {{label}}`},h={newTicket:`Nuevo ticket: {{title}}`,newTicketsAdded_one:`{{count}} ticket nuevo añadido desde product discovery`,newTicketsAdded_other:`{{count}} tickets nuevos añadidos desde product discovery`},g={priority:e,ticketStatus:t,source:n,fields:r,meta:i,untitled:a,deleteDialog:o,detailModal:s,contractLayer:c,rail:l,contextMenu:u,viewMode:d,createModal:f,comparePicker:p,spendingLine:m,toasts:h};export{p as comparePicker,u as contextMenu,c as contractLayer,f as createModal,g as default,o as deleteDialog,s as detailModal,r as fields,i as meta,e as priority,l as rail,n as source,m as spendingLine,t as ticketStatus,h as toasts,a as untitled,d as viewMode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{sn as t}from"./index-
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{sn as t}from"./index-IgMtikGD.js";var n=e(t(),1),r=new Map;function i(e,t){return`${e}:${t}`}function a({namespace:e,projectId:t,initialValue:a,fetcher:o,pollInterval:s=0}){let c=t?i(t,e):null,[l,u]=(0,n.useState)(()=>c&&r.has(c)?r.get(c):a),[d,f]=(0,n.useState)(()=>c?!r.has(c):!0),[p,m]=(0,n.useState)(d),h=(0,n.useRef)(o);h.current=o;let g=(0,n.useRef)(c);return g.current=c,(0,n.useEffect)(()=>{if(!c)return;let e=r.get(c);e===void 0?(u(a),f(!0),m(!0)):(u(e),f(!1),m(!1));let t=!1;async function n(){try{let e=await h.current();if(t)return;r.set(c,e),u(e)}catch{}finally{t||(m(!1),f(!1))}}n();let i;return s>0&&(i=setInterval(n,s)),()=>{t=!0,i&&clearInterval(i)}},[c,s]),{data:l,isLoading:p,isFirstLoad:d,refresh:(0,n.useCallback)(()=>{if(!c)return;let e=c;h.current().then(t=>{r.set(e,t),g.current===e&&u(t)}).catch(()=>{})},[c])}}export{a as t};
|
package/client/dist/index.html
CHANGED
|
@@ -191,7 +191,7 @@
|
|
|
191
191
|
.specrails-splash__mark .pill { opacity: 1; }
|
|
192
192
|
}
|
|
193
193
|
</style>
|
|
194
|
-
<script type="module" crossorigin src="/assets/index-
|
|
194
|
+
<script type="module" crossorigin src="/assets/index-IgMtikGD.js"></script>
|
|
195
195
|
<link rel="modulepreload" crossorigin href="/assets/chunk-CilyBKbf.js">
|
|
196
196
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-DSXbuxSR.js">
|
|
197
197
|
<link rel="modulepreload" crossorigin href="/assets/clsx-CnH-HMk3.js">
|
|
@@ -213,12 +213,12 @@
|
|
|
213
213
|
<link rel="modulepreload" crossorigin href="/assets/jira-C-ATCti0.js">
|
|
214
214
|
<link rel="modulepreload" crossorigin href="/assets/jobs-Db3xrsp_.js">
|
|
215
215
|
<link rel="modulepreload" crossorigin href="/assets/nav-BRInPX8a.js">
|
|
216
|
-
<link rel="modulepreload" crossorigin href="/assets/settings-
|
|
216
|
+
<link rel="modulepreload" crossorigin href="/assets/settings-Cx97tHs5.js">
|
|
217
217
|
<link rel="modulepreload" crossorigin href="/assets/setup-B6egeeTM.js">
|
|
218
218
|
<link rel="modulepreload" crossorigin href="/assets/specs-D-Sb6dre.js">
|
|
219
219
|
<link rel="modulepreload" crossorigin href="/assets/terminal-C0xx0SjA.js">
|
|
220
|
-
<link rel="modulepreload" crossorigin href="/assets/tickets-
|
|
221
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
220
|
+
<link rel="modulepreload" crossorigin href="/assets/tickets-D5MSAPe_.js">
|
|
221
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Cc3LCzBq.css">
|
|
222
222
|
</head>
|
|
223
223
|
<body>
|
|
224
224
|
<div id="specrails-splash" aria-hidden="false" role="status" aria-label="Loading Specrails">
|
package/package.json
CHANGED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CoreUpdateManager = exports.CORE_PACKAGE = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const framework_manager_1 = require("./framework-manager");
|
|
12
|
+
const semver_lite_1 = require("./semver-lite");
|
|
13
|
+
/**
|
|
14
|
+
* CoreUpdateManager — the voluntary, app-global specrails-core update channel.
|
|
15
|
+
*
|
|
16
|
+
* The bundled framework (see FrameworkManager) is pinned to the core version
|
|
17
|
+
* shipped inside the app build. This manager lets a user UPDATE specrails-core
|
|
18
|
+
* independently of the desktop app: it queries npm for the latest published
|
|
19
|
+
* version, and on an explicit request npm-installs that newer core into a temp
|
|
20
|
+
* staging dir, materializes its framework into `~/.specrails/framework/<version>`
|
|
21
|
+
* and atomically swaps `framework/current` to it — every project workspace that
|
|
22
|
+
* symlinks `current/...` jumps to the new core with NO restart and NO per-project
|
|
23
|
+
* `npx`. The previous `framework/<version>` dir is left intact (non-destructive).
|
|
24
|
+
*
|
|
25
|
+
* Detection is deliberately simple (KISS): npm `latest` strictly greater than the
|
|
26
|
+
* installed `framework/current` version ⇒ an update is available. There is no
|
|
27
|
+
* compatibility-range gate; the floor is the app-bundled core (the update only
|
|
28
|
+
* ever moves the pointer UP — FrameworkManager.versionCheck has an anti-downgrade
|
|
29
|
+
* guard so the next startup never reverts a manual update).
|
|
30
|
+
*
|
|
31
|
+
* The whole feature is gated on a bundled core being present (desktop mode); in
|
|
32
|
+
* dev / non-desktop mode `isAvailable()` is false and projects use the legacy npx
|
|
33
|
+
* path, so a core "update" would have no effect and the UI shows "unavailable".
|
|
34
|
+
*/
|
|
35
|
+
exports.CORE_PACKAGE = 'specrails-core';
|
|
36
|
+
const REGISTRY_URL = `https://registry.npmjs.org/${exports.CORE_PACKAGE}/latest`;
|
|
37
|
+
const CHECK_TIMEOUT_MS = 10_000;
|
|
38
|
+
const INSTALL_TIMEOUT_MS = 180_000;
|
|
39
|
+
class CoreUpdateManager {
|
|
40
|
+
home;
|
|
41
|
+
broadcast;
|
|
42
|
+
providersFn;
|
|
43
|
+
fetchLatestFn;
|
|
44
|
+
npmInstallFn;
|
|
45
|
+
makeFrameworkFn;
|
|
46
|
+
latestVersion = null;
|
|
47
|
+
lastCheckedAt = null;
|
|
48
|
+
updating = false;
|
|
49
|
+
constructor(opts = {}) {
|
|
50
|
+
this.home = opts.home;
|
|
51
|
+
this.broadcast = opts.broadcast;
|
|
52
|
+
this.providersFn = opts.providers ?? (() => ['claude']);
|
|
53
|
+
this.fetchLatestFn = opts.fetchLatest ?? (() => fetchLatestFromRegistry());
|
|
54
|
+
this.npmInstallFn = opts.npmInstall ?? defaultNpmInstall;
|
|
55
|
+
this.makeFrameworkFn =
|
|
56
|
+
opts.makeFramework ??
|
|
57
|
+
((coreRoot) => new framework_manager_1.FrameworkManager({ home: this.home, broadcast: this.broadcast, coreRoot }));
|
|
58
|
+
}
|
|
59
|
+
/** A read-only FrameworkManager pointed at the bundled core. */
|
|
60
|
+
bundledFramework() {
|
|
61
|
+
return new framework_manager_1.FrameworkManager({ home: this.home });
|
|
62
|
+
}
|
|
63
|
+
/** True when the bundled-framework system is active (a bundled core is present). */
|
|
64
|
+
isAvailable() {
|
|
65
|
+
return this.bundledFramework().isAvailable();
|
|
66
|
+
}
|
|
67
|
+
/** Current status — no network (uses the cached latest from the last check). */
|
|
68
|
+
getStatus() {
|
|
69
|
+
const fm = this.bundledFramework();
|
|
70
|
+
const bundledVersion = fm.bundledVersion();
|
|
71
|
+
const currentVersion = (0, framework_manager_1.readCurrentFrameworkVersion)(this.home) ?? bundledVersion;
|
|
72
|
+
const updateAvailable = this.latestVersion != null &&
|
|
73
|
+
currentVersion != null &&
|
|
74
|
+
(0, semver_lite_1.isNewer)(this.latestVersion, currentVersion);
|
|
75
|
+
return {
|
|
76
|
+
available: fm.isAvailable(),
|
|
77
|
+
currentVersion,
|
|
78
|
+
bundledVersion,
|
|
79
|
+
latestVersion: this.latestVersion,
|
|
80
|
+
updateAvailable,
|
|
81
|
+
updating: this.updating,
|
|
82
|
+
lastCheckedAt: this.lastCheckedAt,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** Hit npm for the latest published version, cache it, return refreshed status. */
|
|
86
|
+
async checkForUpdate() {
|
|
87
|
+
const latest = await this.fetchLatestFn();
|
|
88
|
+
if (!(0, semver_lite_1.isValidVersion)(latest)) {
|
|
89
|
+
throw new Error(`core-update: npm returned an unparseable version "${latest}"`);
|
|
90
|
+
}
|
|
91
|
+
this.latestVersion = latest.trim().replace(/^[v=]+/, '');
|
|
92
|
+
this.lastCheckedAt = Date.now();
|
|
93
|
+
return this.getStatus();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Materialize + swap to `targetVersion` (default: the cached latest). Streams
|
|
97
|
+
* `core_update.progress` WS events and broadcasts `framework.updated` on
|
|
98
|
+
* success. Returns the outcome; never throws (errors become a failed result +
|
|
99
|
+
* an `error` progress event).
|
|
100
|
+
*/
|
|
101
|
+
async update(targetVersion) {
|
|
102
|
+
if (this.updating) {
|
|
103
|
+
return { ok: false, error: 'An update is already in progress.' };
|
|
104
|
+
}
|
|
105
|
+
if (!this.isAvailable()) {
|
|
106
|
+
return { ok: false, error: 'Core updates are unavailable in this build.' };
|
|
107
|
+
}
|
|
108
|
+
const requested = (targetVersion ?? this.latestVersion ?? '').trim().replace(/^[v=]+/, '');
|
|
109
|
+
if (!requested || !(0, semver_lite_1.isValidVersion)(requested)) {
|
|
110
|
+
return { ok: false, error: 'No valid target version to update to. Check for updates first.' };
|
|
111
|
+
}
|
|
112
|
+
const current = (0, framework_manager_1.readCurrentFrameworkVersion)(this.home);
|
|
113
|
+
if (current && !(0, semver_lite_1.isNewer)(requested, current)) {
|
|
114
|
+
return { ok: false, error: `Already on ${current}; ${requested} is not newer.` };
|
|
115
|
+
}
|
|
116
|
+
this.updating = true;
|
|
117
|
+
this.emit('downloading', { version: requested });
|
|
118
|
+
const tmp = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'core-update-'));
|
|
119
|
+
try {
|
|
120
|
+
this.npmInstallFn(`${exports.CORE_PACKAGE}@${requested}`, tmp);
|
|
121
|
+
const coreRoot = path_1.default.join(tmp, 'node_modules', exports.CORE_PACKAGE);
|
|
122
|
+
const cli = path_1.default.join(coreRoot, 'dist', 'installer', 'cli.js');
|
|
123
|
+
if (!fs_1.default.existsSync(cli)) {
|
|
124
|
+
throw new Error('downloaded core is missing dist/installer/cli.js');
|
|
125
|
+
}
|
|
126
|
+
const fm = this.makeFrameworkFn(coreRoot);
|
|
127
|
+
// The version actually installed (npm may resolve a dist-tag/range).
|
|
128
|
+
const installed = fm.bundledVersion() ?? requested;
|
|
129
|
+
this.emit('materializing', { version: installed });
|
|
130
|
+
const providers = uniqueProviders(this.providersFn());
|
|
131
|
+
const mat = fm.materialize(installed, providers);
|
|
132
|
+
if (!mat.ran) {
|
|
133
|
+
throw new Error('framework materialize did not run (no usable core)');
|
|
134
|
+
}
|
|
135
|
+
if (mat.errors.length > 0) {
|
|
136
|
+
throw new Error(`framework materialize failed: ${mat.errors.map((e) => `${e.provider}: ${e.message}`).join('; ')}`);
|
|
137
|
+
}
|
|
138
|
+
const swapped = fm.swapCurrent(installed);
|
|
139
|
+
if (!swapped) {
|
|
140
|
+
throw new Error('framework swap-current failed');
|
|
141
|
+
}
|
|
142
|
+
this.latestVersion = installed;
|
|
143
|
+
this.lastCheckedAt = Date.now();
|
|
144
|
+
this.emit('done', { version: installed });
|
|
145
|
+
// Reuse the existing app-level event so any listener that reacts to a
|
|
146
|
+
// framework version bump (e.g. core-version banners) refreshes too.
|
|
147
|
+
this.safeBroadcast({ type: 'framework.updated', version: installed });
|
|
148
|
+
return { ok: true, version: installed };
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
152
|
+
this.emit('error', { message });
|
|
153
|
+
return { ok: false, error: message };
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
this.updating = false;
|
|
157
|
+
try {
|
|
158
|
+
fs_1.default.rmSync(tmp, { recursive: true, force: true });
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
/* best-effort temp cleanup */
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
emit(phase, extra) {
|
|
166
|
+
this.safeBroadcast({ type: 'core_update.progress', phase, ...extra });
|
|
167
|
+
}
|
|
168
|
+
safeBroadcast(msg) {
|
|
169
|
+
try {
|
|
170
|
+
this.broadcast?.(msg);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
/* broadcast is best-effort */
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.CoreUpdateManager = CoreUpdateManager;
|
|
178
|
+
function uniqueProviders(values) {
|
|
179
|
+
const out = Array.from(new Set(values.filter((v) => typeof v === 'string' && v.length > 0)));
|
|
180
|
+
return out.length > 0 ? out : ['claude'];
|
|
181
|
+
}
|
|
182
|
+
/** GET the latest published version from the npm registry (no npm binary needed). */
|
|
183
|
+
async function fetchLatestFromRegistry() {
|
|
184
|
+
const controller = new AbortController();
|
|
185
|
+
const timer = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
186
|
+
try {
|
|
187
|
+
const res = await fetch(REGISTRY_URL, {
|
|
188
|
+
signal: controller.signal,
|
|
189
|
+
headers: { accept: 'application/json' },
|
|
190
|
+
});
|
|
191
|
+
if (!res.ok) {
|
|
192
|
+
throw new Error(`npm registry returned ${res.status}`);
|
|
193
|
+
}
|
|
194
|
+
const json = (await res.json());
|
|
195
|
+
if (typeof json.version !== 'string' || json.version.length === 0) {
|
|
196
|
+
throw new Error('npm registry response has no version field');
|
|
197
|
+
}
|
|
198
|
+
return json.version;
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
clearTimeout(timer);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Install `spec` into `cwd` with a minimal isolated package.json — mirrors
|
|
206
|
+
* `scripts/assemble-bundled-core.mjs`. On Windows npm is `npm.cmd`; Node 20.12+
|
|
207
|
+
* (CVE-2024-27980) refuses to spawn a `.cmd` without a shell, so run through the
|
|
208
|
+
* shell there. POSIX spawns directly.
|
|
209
|
+
*/
|
|
210
|
+
function defaultNpmInstall(spec, cwd) {
|
|
211
|
+
fs_1.default.writeFileSync(path_1.default.join(cwd, 'package.json'), JSON.stringify({ name: 'core-update-stage', private: true, version: '0.0.0' }));
|
|
212
|
+
(0, child_process_1.execFileSync)('npm', ['install', spec, '--no-audit', '--no-fund', '--no-save', '--ignore-scripts', '--silent'], {
|
|
213
|
+
cwd,
|
|
214
|
+
encoding: 'utf8',
|
|
215
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
216
|
+
timeout: INSTALL_TIMEOUT_MS,
|
|
217
|
+
shell: process.platform === 'win32',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
@@ -11,6 +11,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
11
11
|
const net_1 = __importDefault(require("net"));
|
|
12
12
|
const desktop_db_1 = require("./desktop-db");
|
|
13
13
|
const webhook_manager_1 = require("./webhook-manager");
|
|
14
|
+
const core_update_manager_1 = require("./core-update-manager");
|
|
14
15
|
const specrails_tech_client_1 = require("./specrails-tech-client");
|
|
15
16
|
const core_compat_1 = require("./core-compat");
|
|
16
17
|
const providers_1 = require("./providers");
|
|
@@ -775,5 +776,47 @@ function createDesktopRouter(registry, broadcast) {
|
|
|
775
776
|
const monthlyBudgetUsd = Number.isFinite(parsed) && parsed >= 0 ? parsed : 5.0;
|
|
776
777
|
res.json({ language, monthlyBudgetUsd });
|
|
777
778
|
});
|
|
779
|
+
// ─── specrails-core update channel (app-global) ─────────────────────────────
|
|
780
|
+
// Detect + apply a specrails-core framework update independently of the desktop
|
|
781
|
+
// app update. See server/core-update-manager.ts. A single manager instance
|
|
782
|
+
// persists for the router lifetime (holds the cached latest + in-progress flag).
|
|
783
|
+
const coreUpdate = new core_update_manager_1.CoreUpdateManager({
|
|
784
|
+
// `core_update.progress` / `framework.updated` are app-level (no projectId)
|
|
785
|
+
// and not members of the WsMessage union; cast at this single boundary.
|
|
786
|
+
broadcast: (msg) => broadcast(msg),
|
|
787
|
+
providers: () => registry.installedProvidersUnion(),
|
|
788
|
+
});
|
|
789
|
+
// GET /api/core-update/status — current/bundled/latest versions, no network.
|
|
790
|
+
router.get('/core-update/status', (_req, res) => {
|
|
791
|
+
res.json(coreUpdate.getStatus());
|
|
792
|
+
});
|
|
793
|
+
// POST /api/core-update/check — hit npm for the latest version, refresh status.
|
|
794
|
+
router.post('/core-update/check', (_req, res) => {
|
|
795
|
+
void coreUpdate
|
|
796
|
+
.checkForUpdate()
|
|
797
|
+
.then((status) => res.json(status))
|
|
798
|
+
.catch((err) => {
|
|
799
|
+
const message = err instanceof Error ? err.message : 'check failed';
|
|
800
|
+
res.status(502).json({ error: 'check_failed', message });
|
|
801
|
+
});
|
|
802
|
+
});
|
|
803
|
+
// POST /api/core-update/update — materialize + swap to the target (default latest).
|
|
804
|
+
// 202 + async progress over the `core_update.progress` WS event.
|
|
805
|
+
router.post('/core-update/update', (req, res) => {
|
|
806
|
+
if (!coreUpdate.isAvailable()) {
|
|
807
|
+
res.status(409).json({ error: 'unavailable', message: 'Core updates are unavailable in this build.' });
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
const status = coreUpdate.getStatus();
|
|
811
|
+
if (status.updating) {
|
|
812
|
+
res.status(409).json({ error: 'in_progress', message: 'An update is already in progress.' });
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
const version = req.body?.version;
|
|
816
|
+
const target = typeof version === 'string' ? version : undefined;
|
|
817
|
+
res.status(202).json({ accepted: true });
|
|
818
|
+
// Fire-and-forget; outcome is delivered over WS (`core_update.progress`).
|
|
819
|
+
void coreUpdate.update(target);
|
|
820
|
+
});
|
|
778
821
|
return router;
|
|
779
822
|
}
|
|
@@ -12,6 +12,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
12
12
|
const bundled_core_1 = require("./bundled-core");
|
|
13
13
|
const artifact_registry_1 = require("./artifact-registry");
|
|
14
14
|
Object.defineProperty(exports, "atomicWrite", { enumerable: true, get: function () { return artifact_registry_1.atomicWrite; } });
|
|
15
|
+
const semver_lite_1 = require("./semver-lite");
|
|
15
16
|
/** `~/.specrails/framework` — same home as the registry. */
|
|
16
17
|
function frameworkRoot(home) {
|
|
17
18
|
return path_1.default.join((0, artifact_registry_1.resolveHome)(home), '.specrails', 'framework');
|
|
@@ -50,16 +51,43 @@ function readCurrentFrameworkVersion(home) {
|
|
|
50
51
|
class FrameworkManager {
|
|
51
52
|
home;
|
|
52
53
|
broadcast;
|
|
54
|
+
coreRoot;
|
|
53
55
|
constructor(opts = {}) {
|
|
54
56
|
this.home = opts.home;
|
|
55
57
|
this.broadcast = opts.broadcast;
|
|
58
|
+
this.coreRoot = opts.coreRoot;
|
|
56
59
|
}
|
|
57
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Resolve the core CLI to shell out to: the override core root (update channel)
|
|
62
|
+
* when set, else the bundled core. Returns null when neither is present.
|
|
63
|
+
*/
|
|
64
|
+
resolveCli() {
|
|
65
|
+
if (this.coreRoot) {
|
|
66
|
+
const cli = path_1.default.join(this.coreRoot, 'dist', 'installer', 'cli.js');
|
|
67
|
+
return fs_1.default.existsSync(cli) ? cli : null;
|
|
68
|
+
}
|
|
69
|
+
return (0, bundled_core_1.getBundledCoreCli)();
|
|
70
|
+
}
|
|
71
|
+
/** True when a usable core (override or bundled) is present (else methods no-op). */
|
|
58
72
|
isAvailable() {
|
|
59
|
-
return
|
|
73
|
+
return this.resolveCli() !== null;
|
|
60
74
|
}
|
|
61
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* The version the app should materialize: the override core's version (update
|
|
77
|
+
* channel) when a `coreRoot` is set, else the bundled core version. Null when
|
|
78
|
+
* neither is present / unreadable.
|
|
79
|
+
*/
|
|
62
80
|
bundledVersion() {
|
|
81
|
+
if (this.coreRoot) {
|
|
82
|
+
try {
|
|
83
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(this.coreRoot, 'package.json'), 'utf8'));
|
|
84
|
+
const v = pkg.version?.trim();
|
|
85
|
+
return v && v.length > 0 ? v : null;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
63
91
|
return (0, bundled_core_1.getBundledCoreVersion)();
|
|
64
92
|
}
|
|
65
93
|
/**
|
|
@@ -73,7 +101,7 @@ class FrameworkManager {
|
|
|
73
101
|
* (ran:false) when no bundled core is present.
|
|
74
102
|
*/
|
|
75
103
|
materialize(version, providers = ['claude']) {
|
|
76
|
-
const cli =
|
|
104
|
+
const cli = this.resolveCli();
|
|
77
105
|
if (!cli) {
|
|
78
106
|
return { ran: false, version: null, providers: [], errors: [] };
|
|
79
107
|
}
|
|
@@ -119,7 +147,7 @@ class FrameworkManager {
|
|
|
119
147
|
* `swap-current` only moves the version pointer (provider-invariant).
|
|
120
148
|
*/
|
|
121
149
|
swapCurrent(version, _provider = 'claude') {
|
|
122
|
-
const cli =
|
|
150
|
+
const cli = this.resolveCli();
|
|
123
151
|
if (!cli)
|
|
124
152
|
return false;
|
|
125
153
|
if (readCurrentFrameworkVersion(this.home) === version)
|
|
@@ -138,13 +166,22 @@ class FrameworkManager {
|
|
|
138
166
|
*/
|
|
139
167
|
versionCheck(providers = ['claude']) {
|
|
140
168
|
const bundled = this.bundledVersion();
|
|
141
|
-
if (!
|
|
169
|
+
if (!this.resolveCli() || !bundled) {
|
|
142
170
|
return { swapped: false, version: readCurrentFrameworkVersion(this.home) };
|
|
143
171
|
}
|
|
144
172
|
const current = readCurrentFrameworkVersion(this.home);
|
|
145
173
|
if (current === bundled) {
|
|
146
174
|
return { swapped: false, version: current };
|
|
147
175
|
}
|
|
176
|
+
// Anti-downgrade guard: NEVER swap `current` down to the bundled version when
|
|
177
|
+
// `current` is already NEWER. The core update channel lets a user voluntarily
|
|
178
|
+
// materialize a core newer than the one shipped in this app build; without
|
|
179
|
+
// this guard the next startup versionCheck would revert that manual update
|
|
180
|
+
// back to the (older) bundled version. Only first-run (current null) or a
|
|
181
|
+
// genuinely newer bundled core (a fresh app update) proceeds.
|
|
182
|
+
if (current && !(0, semver_lite_1.isNewer)(bundled, current)) {
|
|
183
|
+
return { swapped: false, version: current };
|
|
184
|
+
}
|
|
148
185
|
// Materialize EVERY requested provider first (each with `--no-swap`), then
|
|
149
186
|
// swap `current` ONCE — and ONLY when every provider materialized cleanly.
|
|
150
187
|
// A partial multi-provider install must NEVER flip `current`: doing so would
|
|
@@ -201,7 +238,7 @@ class FrameworkManager {
|
|
|
201
238
|
* when no bundled core is present — caller falls back to legacy npx assembly.
|
|
202
239
|
*/
|
|
203
240
|
assembleWorkspace(input) {
|
|
204
|
-
const cli =
|
|
241
|
+
const cli = this.resolveCli();
|
|
205
242
|
if (!cli)
|
|
206
243
|
return { ran: false };
|
|
207
244
|
const ver = input.version ?? this.bundledVersion();
|
|
@@ -234,9 +271,10 @@ class FrameworkManager {
|
|
|
234
271
|
if (this.home) {
|
|
235
272
|
env.SPECRAILS_REGISTRY_HOME = this.home;
|
|
236
273
|
}
|
|
237
|
-
// Point the
|
|
238
|
-
// template/command sources resolve from the
|
|
239
|
-
|
|
274
|
+
// Point the core CLI's scriptDir at the package whose framework we are
|
|
275
|
+
// materializing so its template/command sources resolve from there: the
|
|
276
|
+
// OVERRIDE core (update channel) when set, else the bundled package.
|
|
277
|
+
const coreRoot = this.coreRoot ?? process.env.SPECRAILS_BUNDLED_CORE_PATH;
|
|
240
278
|
if (coreRoot)
|
|
241
279
|
env.SPECRAILS_CORE_SCRIPT_DIR = coreRoot;
|
|
242
280
|
return env;
|