tabby-fknat 0.1.2 → 0.1.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.
package/README.md CHANGED
@@ -81,6 +81,10 @@ Windows 默认路径是 `%APPDATA%\\tabby\\config.yaml`;只读属性会阻止
81
81
 
82
82
  ## 发布包
83
83
 
84
+ Tabby 自带 Angular、RxJS 和各个原生插件。它们声明为 optional peer dependencies,
85
+ 让插件继续使用宿主提供的模块,并阻止市场安装器另行下载 npm 上的 Tabby 内置插件。
86
+ `0.1.3` 修复市场安装时自动安装 `tabby-ssh` 并在其 `postinstall` 阶段失败的问题。
87
+
84
88
  `npm pack` 会运行 TypeScript 验证,并重新构建全部六个辅助程序目标。除 npm 包元数据
85
89
  外,发布包仅包含 `dist/`、`bin/` 和本 README。`bin/manifest.json` 记录每个原生辅助
86
90
  程序的 SHA-256 校验和;插件会在启动前验证所选的内置二进制文件。
@@ -98,3 +102,13 @@ modfile 引用该副本、执行权限回归测试;Unix 的权限检查继续
98
102
  每次发布请递增版本号。`0.1.1` 包含原生 SSH 提供器调用、Windows 权限兼容及独立文件
99
103
  日志修复;启动日志会记录实际包版本与加载目录,helper 版本也与包版本一致。
100
104
  不要重新安装先前的 `tabby-fknat-0.1.0.tgz`,它可能覆盖这些修复。
105
+
106
+ 打包后必须验证真实归档的默认 npm 安装,保留生命周期脚本与默认 peer 解析:
107
+
108
+ ```sh
109
+ npm pack
110
+ npm run verify:package -- tabby-fknat-0.1.3.tgz
111
+ ```
112
+
113
+ 此检查在空目录中安装发布包,确认未下载第二份 Tabby/Angular,并验证六个平台的 helper
114
+ 校验和。不能用开发时的 `--legacy-peer-deps` 或 `--ignore-scripts` 代替该发布检查。
Binary file
Binary file
Binary file
Binary file
package/bin/manifest.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
- "win32-x64": "sha256:25c4b4288a4c7fe24649df889485487ec4ca35beed8a6c15902171c8700d66a7",
3
- "win32-arm64": "sha256:882db63874312aba16b40f3b0ed25819a267d14688d7b1f09e8344c1006a1ec2",
4
- "linux-x64": "sha256:83d5ae6f83844d8b1345d6717e958075a72cb8cc88ea2b80836c648c42a4c428",
5
- "linux-arm64": "sha256:355f01ec535e25af14748a0186842cd204b1e505b0301216adac236cc090962e",
6
- "darwin-x64": "sha256:cc7a67b7b47552f34faeff624f96d82eb43c3de82cbca764147bc96a24d99b9f",
7
- "darwin-arm64": "sha256:c6d618114df9f3d4104eed6ef77b1c4a22de2e3ee4c6a64fd7e6f4540b204fe2"
2
+ "win32-x64": "sha256:44ea6f177419fa3f4b3562f8d07713738646aec349d70c319915a30e46094e08",
3
+ "win32-arm64": "sha256:51975c25feb38332fdf78fc28b2e82484e472e032ba4a1653bb5fcbea65c204e",
4
+ "linux-x64": "sha256:36f701d9bbf6348aec2a54210765264ced0c419ddb9f46acbe8e42e46a960158",
5
+ "linux-arm64": "sha256:9e580b52cf10e7ad3f9d36275b72cc71c55113cc756ae6f30a6e228c959fafc6",
6
+ "darwin-x64": "sha256:9f4a4a68bba008197a2c3e9651fcbd0d9fe4613774e4e7d3552785e60ca5e3e1",
7
+ "darwin-arm64": "sha256:87bb192ba6a0c78118f15b1715076b9a3833077501cd8802d026062ee3010b79"
8
8
  }
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- (()=>{"use strict";var e={966(e,t,r){var i=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},o=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATProfileSettingsComponent=void 0;const s=r(976),a=r(438),l=r(922);let c=class{constructor(e){this.i18n=e,this.addressError=""}get privateKey(){return this.profile.options.privateKeys?.[0]??""}set privateKey(e){this.profile.options.privateKeys=e.trim()?[e.trim()]:[]}save(){(0,l.isFKNATAddress)(this.profile.options.address)?(this.profile.options.address=(0,l.normalizeFKNATAddress)(this.profile.options.address),this.profile.options.port=this.profile.options.port||22,this.profile.options.user=this.profile.options.user?.trim()||"root",this.profile.options.fknatProfileVersion=1):this.addressError=this.i18n.instant("Address must be exactly 64 hexadecimal characters.")}};c=i([(0,s.Component)({selector:"fknat-profile-settings",template:'\n <div class="form-group">\n <label translate>FKNAT Address</label>\n <input class="form-control" [(ngModel)]="profile.options.address" [placeholder]="\'64-character FKNAT Address\' | translate">\n <small class="form-text text-muted" *ngIf="addressError">{{ addressError }}</small>\n </div>\n <div class="form-group">\n <label translate>SSH port</label>\n <input class="form-control" type="number" [(ngModel)]="profile.options.port" min="1" max="65535">\n </div>\n <div class="form-group">\n <label translate>SSH username</label>\n <input class="form-control" [(ngModel)]="profile.options.user" autocomplete="username">\n </div>\n <div class="form-group">\n <label translate>Authentication</label>\n <select class="form-control" [(ngModel)]="profile.options.auth">\n <option [ngValue]="null" translate>Automatic</option>\n <option value="password" translate>Password</option>\n <option value="publicKey" translate>Public key</option>\n <option value="agent" translate>SSH agent</option>\n <option value="keyboardInteractive" translate>Keyboard interactive</option>\n </select>\n </div>\n <small class="form-text text-muted" *ngIf="profile.options.auth === \'password\'" translate>\n Tabby will prompt for the SSH password and may save it in its native keychain.\n </small>\n <div class="form-group" *ngIf="profile.options.auth === \'publicKey\'">\n <label translate>Private key</label>\n <input class="form-control" [(ngModel)]="privateKey" placeholder="~/.ssh/id_ed25519">\n </div>\n <p class="text-muted" translate>FKNAT route 0 and the helper transport are managed automatically.</p>\n '}),n(0,(0,s.Inject)(a.FKNAT_TRANSLATION_SERVICE)),o("design:paramtypes",[Object])],c),t.FKNATProfileSettingsComponent=c},786(e,t,r){var i=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},o=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATSettingsTabProvider=t.FKNATSettingsTabComponent=void 0;const s=r(976),a=r(414),l=r(120),c=r(438),d=r(551),p=r(372);let h=class{constructor(e,t,r,i){this.billingService=e,this.helper=t,this.i18n=r,this.configured=!1,this.busy=!1,this.message="",this.log=i.create("tabby-fknat.settings"),this.log.debug("FKNAT settings component constructed"),this.refresh().catch(e=>{this.log.error("FKNAT settings refresh failed",{error:u(e)}),this.message=this.i18n.instant("FKNAT diagnostics failed.")})}async refresh(){this.log.debug("Refreshing FKNAT settings state"),this.configured=await this.billingService.isConfigured(),this.log.info("FKNAT settings state refreshed",{configured:this.configured})}async importBilling(e){const t=e.files?.[0];if(t){this.busy=!0,this.message="",this.log.info("Importing FKNAT billing key",{size:t.size,name:t.name});try{const e=await this.billingService.importFromBytes(new Uint8Array(await t.arrayBuffer()));this.message=this.i18n.instant("Billing key imported."),this.log.info("FKNAT billing key imported",{destination:e,size:t.size}),await this.refresh()}catch(e){this.log.error("FKNAT billing key import failed",{error:u(e)}),this.message=this.i18n.instant(e instanceof Error?e.message:"Billing key import failed.")}finally{this.busy=!1,e.value=""}}}async runDoctor(){this.busy=!0,this.message="",this.log.info("FKNAT settings diagnostics requested");try{const e=await this.helper.doctor();this.log.info("FKNAT settings diagnostics completed",e),this.message=e.ok?this.i18n.instant("FKNAT network: {status}",{status:e.network_status??this.i18n.instant("ready")}):this.i18n.instant("FKNAT diagnostics failed.")}catch(e){this.log.error("FKNAT settings diagnostics failed",{error:u(e)}),this.message=this.i18n.instant(e instanceof Error?e.message:"FKNAT diagnostics failed.")}finally{this.busy=!1}}};function u(e){return e instanceof Error?`${e.name}: ${e.message}`:String(e)}h=i([(0,s.Component)({selector:"fknat-settings-tab",template:'\n <h3 translate>FKNAT Network</h3>\n <p *ngIf="configured" class="text-success" translate>Billing authorization: Ready</p>\n <p *ngIf="!configured" class="text-warning" translate>FKNAT billing key is not configured.</p>\n <input #billingInput type="file" accept="application/json,.json" hidden (change)="importBilling(billingInput)">\n <button class="btn btn-primary" type="button" (click)="billingInput.click()">\n {{ (configured ? \'Replace billing key\' : \'Import billing key\') | translate }}\n </button>\n <button class="btn btn-secondary ml-2" type="button" (click)="runDoctor()" [disabled]="busy" translate>Run diagnostics</button>\n <p *ngIf="message" class="mt-3">{{ message }}</p>\n <small class="text-muted" translate>The private key is stored locally and is not included in SSH profiles.</small>\n '}),n(0,(0,s.Inject)(d.BillingService)),n(1,(0,s.Inject)(p.HelperService)),n(2,(0,s.Inject)(c.FKNAT_TRANSLATION_SERVICE)),n(3,(0,s.Inject)(a.LogService)),o("design:paramtypes",[d.BillingService,p.HelperService,Object,a.LogService])],h),t.FKNATSettingsTabComponent=h;let f=class extends l.SettingsTabProvider{constructor(){super(...arguments),this.id="fknat",this.icon="fas fa-network-wired",this.title="FKNAT",this.weight=20,this.prioritized=!1}getComponentType(){return h}};f=i([(0,s.Injectable)()],f),t.FKNATSettingsTabProvider=f},438(e,t){t.FKNAT_TRANSLATIONS=t.FKNAT_TRANSLATION_SERVICE=void 0,t.FKNAT_TRANSLATION_SERVICE="tabby-fknat.translation-service",t.FKNAT_TRANSLATIONS={"zh-CN":{"FKNAT Network":"FKNAT 网络","Billing authorization: Ready":"扣费授权:已就绪","FKNAT billing key is not configured.":"尚未配置 FKNAT 扣费密钥。","Replace billing key":"替换扣费密钥","Import billing key":"导入扣费密钥","Run diagnostics":"运行诊断","The private key is stored locally and is not included in SSH profiles.":"私钥仅保存在本机,不会写入 SSH 配置。","Billing key imported.":"扣费密钥已导入。","Billing key import failed.":"扣费密钥导入失败。","FKNAT network: {status}":"FKNAT 网络:{status}",ready:"已就绪","FKNAT diagnostics failed.":"FKNAT 诊断失败。","FKNAT Address":"FKNAT 地址","64-character FKNAT Address":"64 位 FKNAT 地址","SSH port":"SSH 端口","SSH username":"SSH 用户名",Authentication:"身份验证",Automatic:"自动",Password:"密码","Public key":"公钥","SSH agent":"SSH Agent","Keyboard interactive":"键盘交互","Tabby will prompt for the SSH password and may save it in its native keychain.":"Tabby 将提示输入 SSH 密码,并可能将其保存在系统钥匙串中。","Private key":"私钥","FKNAT route 0 and the helper transport are managed automatically.":"FKNAT 路由 0 和 helper 传输由插件自动管理。","Address must be exactly 64 hexadecimal characters.":"地址必须正好包含 64 个十六进制字符。","FKNAT SSH connection":"FKNAT SSH 连接","The native Tabby SSH plugin is not available. Enable it and restart Tabby.":"Tabby 原生 SSH 插件不可用,请启用该插件并重启 Tabby。","Unsupported FKNAT helper protocol version {version}":"不支持 FKNAT helper 协议版本 {version}","Billing key file must be between 1 and 65536 bytes":"扣费密钥文件大小必须介于 1 到 65536 字节之间","Billing key file must contain valid JSON":"扣费密钥文件必须包含有效的 JSON","Billing key JSON must be an object":"扣费密钥 JSON 必须是一个对象","FKNAT helper path is not a file":"FKNAT helper 路径不是文件","FKNAT helper is not executable":"FKNAT helper 不可执行","FKNAT helper checksum is missing for {platform}":"缺少 {platform} 平台的 FKNAT helper 校验和","FKNAT helper checksum verification failed":"FKNAT helper 校验和验证失败","FKNAT helper exited with status {status}":"FKNAT helper 已退出,状态码为 {status}","FKNAT helper command timed out after {seconds}s":"FKNAT helper 在 {seconds} 秒后超时","FKNAT helper does not support 32-bit Windows":"FKNAT helper 不支持 32 位 Windows","Unsupported FKNAT helper platform: {platform}":"不支持的 FKNAT helper 平台:{platform}","The FKNAT helper received invalid connection settings.":"FKNAT helper 收到了无效的连接设置。","Your FKNAT billing authorization is invalid. Import a new billing key.":"FKNAT 扣费授权无效,请导入新的扣费密钥。","The local FKNAT client identity could not be loaded or created.":"无法加载或创建本地 FKNAT 客户端身份。","The FKNAT Address is invalid.":"FKNAT 地址无效。","FKNAT network admission failed.":"FKNAT 网络准入失败。","FKNAT network discovery failed.":"FKNAT 网络发现失败。","The FKNAT target is currently offline.":"FKNAT 目标当前离线。","The target is online but does not expose the default FKNAT SSH route.":"目标在线,但未开放默认的 FKNAT SSH 路由。","The FKNAT target is reachable, but its SSH route is not available.":"FKNAT 目标可达,但 SSH 路由当前不可用。","The FKNAT network connection failed.":"FKNAT 网络连接失败。","FKNAT billing authorization failed.":"FKNAT 扣费授权失败。","The FKNAT helper failed unexpectedly.":"FKNAT helper 意外失败。"},"zh-TW":{"FKNAT Network":"FKNAT 網路","Billing authorization: Ready":"計費授權:已就緒","FKNAT billing key is not configured.":"尚未設定 FKNAT 計費金鑰。","Replace billing key":"替換計費金鑰","Import billing key":"匯入計費金鑰","Run diagnostics":"執行診斷","The private key is stored locally and is not included in SSH profiles.":"私鑰僅儲存在本機,不會寫入 SSH 設定檔。","Billing key imported.":"計費金鑰已匯入。","Billing key import failed.":"計費金鑰匯入失敗。","FKNAT network: {status}":"FKNAT 網路:{status}",ready:"已就緒","FKNAT diagnostics failed.":"FKNAT 診斷失敗。","FKNAT Address":"FKNAT 位址","64-character FKNAT Address":"64 位 FKNAT 位址","SSH port":"SSH 連接埠","SSH username":"SSH 使用者名稱",Authentication:"身分驗證",Automatic:"自動",Password:"密碼","Public key":"公開金鑰","SSH agent":"SSH Agent","Keyboard interactive":"鍵盤互動","Tabby will prompt for the SSH password and may save it in its native keychain.":"Tabby 將提示輸入 SSH 密碼,並可能將其儲存在系統鑰匙圈中。","Private key":"私鑰","FKNAT route 0 and the helper transport are managed automatically.":"FKNAT 路由 0 和 helper 傳輸由外掛程式自動管理。","Address must be exactly 64 hexadecimal characters.":"位址必須正好包含 64 個十六進位字元。","FKNAT SSH connection":"FKNAT SSH 連線","The native Tabby SSH plugin is not available. Enable it and restart Tabby.":"Tabby 原生 SSH 外掛程式無法使用,請啟用該外掛程式並重新啟動 Tabby。","Unsupported FKNAT helper protocol version {version}":"不支援 FKNAT helper 通訊協定版本 {version}","Billing key file must be between 1 and 65536 bytes":"計費金鑰檔案大小必須介於 1 到 65536 位元組之間","Billing key file must contain valid JSON":"計費金鑰檔案必須包含有效的 JSON","Billing key JSON must be an object":"計費金鑰 JSON 必須是物件","FKNAT helper path is not a file":"FKNAT helper 路徑不是檔案","FKNAT helper is not executable":"FKNAT helper 無法執行","FKNAT helper checksum is missing for {platform}":"缺少 {platform} 平台的 FKNAT helper 檢查碼","FKNAT helper checksum verification failed":"FKNAT helper 檢查碼驗證失敗","FKNAT helper exited with status {status}":"FKNAT helper 已結束,狀態碼為 {status}","FKNAT helper command timed out after {seconds}s":"FKNAT helper 在 {seconds} 秒後逾時","FKNAT helper does not support 32-bit Windows":"FKNAT helper 不支援 32 位元 Windows","Unsupported FKNAT helper platform: {platform}":"不支援的 FKNAT helper 平台:{platform}","The FKNAT helper received invalid connection settings.":"FKNAT helper 收到無效的連線設定。","Your FKNAT billing authorization is invalid. Import a new billing key.":"FKNAT 計費授權無效,請匯入新的計費金鑰。","The local FKNAT client identity could not be loaded or created.":"無法載入或建立本機 FKNAT 用戶端身分。","The FKNAT Address is invalid.":"FKNAT 位址無效。","FKNAT network admission failed.":"FKNAT 網路准入失敗。","FKNAT network discovery failed.":"FKNAT 網路探索失敗。","The FKNAT target is currently offline.":"FKNAT 目標目前離線。","The target is online but does not expose the default FKNAT SSH route.":"目標在線上,但未開放預設的 FKNAT SSH 路由。","The FKNAT target is reachable, but its SSH route is not available.":"FKNAT 目標可達,但 SSH 路由目前無法使用。","The FKNAT network connection failed.":"FKNAT 網路連線失敗。","FKNAT billing authorization failed.":"FKNAT 計費授權失敗。","The FKNAT helper failed unexpectedly.":"FKNAT helper 發生未預期的錯誤。"}}},156(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||i(t,e,r)},n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var s=r(858);Object.defineProperty(t,"default",{enumerable:!0,get:function(){return n(s).default}}),o(r(490),t),o(r(372),t),o(r(551),t)},665(e,t){t.defaultFKNATConfig=void 0,t.defaultFKNATConfig={configVersion:1,helperPathOverride:null,logLevel:"warn",connectionTimeoutSeconds:30}},490(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.parseFKNATQuickConnect=t.toNativeSSHProfile=t.TABBY_SSH_DEFAULT_ALGORITHMS=void 0;const i=r(922);t.TABBY_SSH_DEFAULT_ALGORITHMS={kex:["mlkem768x25519-sha256","curve25519-sha256","curve25519-sha256@libssh.org","diffie-hellman-group16-sha512","diffie-hellman-group14-sha256","ext-info-c","ext-info-s","kex-strict-c-v00@openssh.com","kex-strict-s-v00@openssh.com"],serverHostKey:["ssh-ed25519","ecdsa-sha2-nistp256","ecdsa-sha2-nistp521","rsa-sha2-256","rsa-sha2-512","ssh-rsa"],cipher:["chacha20-poly1305@openssh.com","aes256-gcm@openssh.com","aes256-ctr","aes192-ctr","aes128-ctr"],hmac:["hmac-sha2-512-etm@openssh.com","hmac-sha2-256-etm@openssh.com","hmac-sha2-512","hmac-sha2-256","hmac-sha1-etm@openssh.com","hmac-sha1"],compression:["none"]},t.toNativeSSHProfile=function(e,r){const o=(0,i.normalizeFKNATAddress)(e.options.address);return{...e,type:"ssh",clearServiceMessagesOnConnect:e.clearServiceMessagesOnConnect??!0,options:{host:o,port:e.options.port??22,user:e.options.user||"root",auth:e.options.auth??null,password:e.options.password,privateKeys:e.options.privateKeys??[],keepaliveInterval:e.options.keepaliveInterval??5e3,keepaliveCountMax:e.options.keepaliveCountMax??10,readyTimeout:e.options.readyTimeout,x11:e.options.x11??!1,skipBanner:e.options.skipBanner??!1,agentForward:e.options.agentForward??!1,warnOnClose:e.options.warnOnClose,algorithms:(n=e.options.algorithms,Object.fromEntries(Object.entries(t.TABBY_SSH_DEFAULT_ALGORITHMS).map(([e,t])=>[e,[...n?.[e]??t]]))),proxyCommand:r,forwardedPorts:e.options.forwardedPorts??[],scripts:e.options.scripts??[],reuseSession:e.options.reuseSession??!0,input:e.options.input??{backspace:"backspace"}}};var n},t.parseFKNATQuickConnect=function(e){const t=/^fknat:\/\/(?:([^@/?#]+)@)?([^/?#]+)\/?$/i.exec(e.trim());if(!t)return null;let r,o;try{r=(0,i.normalizeFKNATAddress)(t[2])}catch{return null}try{o=t[1]?decodeURIComponent(t[1]):"root"}catch{return null}return!o||/[\x00-\x1f\x7f]/.test(o)?null:{type:"fknat-ssh",name:`${o}@${r.slice(0,12)}...`,options:{address:r,port:22,user:o,auth:null,privateKeys:[],fknatProfileVersion:1}}}},858(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&i(t,e,r);return o(t,e),t},a=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},l=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}},c=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const d=r(976),p=r(282),h=r(98),u=s(r(414)),f=s(r(120)),g=c(r(265)),m=r(966),T=r(786),b=r(438),A=r(665),v=r(459),N=r(551),y=r(372),F=r(640),K=r(95);class S extends u.ConfigProvider{constructor(){super(...arguments),this.defaults={fknat:A.defaultFKNATConfig},this.platformDefaults={}}}let w=class{constructor(e,t){this.translations=e,t.create("tabby-fknat.module").info("FKNAT Tabby plugin module loaded",{pluginVersion:"0.1.2",moduleDirectory:__dirname,platform:process.platform,arch:process.arch})}};w=n([(0,d.NgModule)({imports:[p.CommonModule,h.FormsModule,u.default,f.default,g.default],providers:[K.FKNATPlatformService,N.BillingService,F.FKNATTranslationService,{provide:b.FKNAT_TRANSLATION_SERVICE,useExisting:F.FKNATTranslationService},{provide:y.HelperService,useFactory:(e,t,r,i)=>new y.HelperService(e,t,r,i.create("tabby-fknat.helper")),deps:[K.FKNATPlatformService,u.ConfigService,b.FKNAT_TRANSLATION_SERVICE,u.LogService]},{provide:u.ConfigProvider,useClass:S,multi:!0},{provide:u.ProfileProvider,useExisting:v.FKNATProfileProvider,multi:!0},{provide:v.FKNATProfileProvider,useClass:v.FKNATProfileProvider},{provide:f.SettingsTabProvider,useClass:T.FKNATSettingsTabProvider,multi:!0}],declarations:[m.FKNATProfileSettingsComponent,T.FKNATSettingsTabComponent]}),l(0,(0,d.Inject)(F.FKNATTranslationService)),l(1,(0,d.Inject)(u.LogService)),a("design:paramtypes",[F.FKNATTranslationService,u.LogService])],w),t.default=w},459(e,t,r){var i=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},o=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATProfileProvider=void 0;const s=r(976),a=r(414),l=r(966),c=r(438),d=r(490),p=r(372);let h=class extends a.QuickConnectProfileProvider{constructor(e,t,r,i){super(),this.helper=e,this.i18n=t,this.injector=i,this.id="fknat-ssh",this.name="FKNAT SSH",this.settingsComponent=l.FKNATProfileSettingsComponent,this.configDefaults={options:{address:"",port:22,user:"root",auth:null,password:null,privateKeys:[],agentForward:!1,forwardedPorts:[],fknatProfileVersion:1},clearServiceMessagesOnConnect:!0},this.log=r.create("tabby-fknat.profile"),this.log.debug("FKNAT profile provider constructed")}async getBuiltinProfiles(){return[{id:"fknat-ssh:template",type:this.id,name:this.i18n.instant("FKNAT SSH connection"),icon:"fas fa-network-wired",isBuiltin:!0,isTemplate:!0,weight:-1,options:{address:"",port:22,user:"root",auth:null,privateKeys:[],fknatProfileVersion:1}}]}async getNewTabParameters(e){this.log.info("Preparing FKNAT SSH tab",{address:u(e.options.address),user:e.options.user||"root",port:e.options.port,auth:e.options.auth??"automatic",configuredPrivateKeyCount:e.options.privateKeys?.length??0,privateKeySource:e.options.auth&&"publicKey"!==e.options.auth?"disabled by authentication mode":e.options.privateKeys?.length?"configured":"automatic discovery"}),await this.helper.assertExecutable();const t=await this.helper.version();if(this.log.debug("FKNAT helper protocol checked",t),1!==t.protocol_version)throw new Error(this.i18n.instant("Unsupported FKNAT helper protocol version {version}",{version:t.protocol_version}));const r=(0,d.toNativeSSHProfile)(e,this.helper.buildProxyCommand(e.options.address)),i=this.injector.get(a.ProfilesService),o=i.providerForProfile(r);if(!o)throw this.log.error("Tabby native SSH profile provider is unavailable"),new Error(this.i18n.instant("The native Tabby SSH plugin is not available. Enable it and restart Tabby."));const n=await o.getNewTabParameters(i.getConfigProxyForProfile(r));return this.log.info("FKNAT SSH tab parameters ready",{address:u(e.options.address),helperVersion:t.helper_version,protocolVersion:t.protocol_version,sshProvider:o.constructor.name,sshComponent:n.type.name,auth:r.options.auth??"automatic",configuredPrivateKeyCount:r.options.privateKeys?.length??0}),n}getSuggestedName(e){return`${e.options?.user||"root"}@${e.options?.address||"FKNAT"}`}getDescription(e){return e.options?.address??""}quickConnect(e){return(0,d.parseFKNATQuickConnect)(e)}intoQuickConnectString(e){return`fknat://${e.options.user||"root"}@${e.options.address}`}};function u(e){return e?e.length<=16?e:`${e.slice(0,8)}…${e.slice(-8)}`:"<empty>"}h=i([(0,s.Injectable)({providedIn:"root"}),n(0,(0,s.Inject)(p.HelperService)),n(1,(0,s.Inject)(c.FKNAT_TRANSLATION_SERVICE)),n(2,(0,s.Inject)(a.LogService)),n(3,(0,s.Inject)(s.Injector)),o("design:paramtypes",[p.HelperService,Object,a.LogService,s.Injector])],h),t.FKNATProfileProvider=h},551(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&i(t,e,r);return o(t,e),t},a=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},l=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BillingService=void 0;const c=s(r(524)),d=s(r(697)),p=r(976),h=r(95);let u=class{constructor(e){this.platform=e}getPath(){return this.platform.getBillingPath()}async isConfigured(){try{const e=await c.stat(this.getPath());return e.isFile()&&e.size>0&&e.size<=65536}catch{return!1}}async importFromBytes(e){if(0===e.length||e.length>65536)throw new Error("Billing key file must be between 1 and 65536 bytes");const t=(new TextDecoder).decode(e);let r;try{r=JSON.parse(t)}catch{throw new Error("Billing key file must contain valid JSON")}if(!r||"object"!=typeof r||Array.isArray(r))throw new Error("Billing key JSON must be an object");const i=this.getPath(),o=d.dirname(i);await c.mkdir(o,{recursive:!0,mode:448}),"win32"!==process.platform&&(await c.chmod(d.dirname(o),448),await c.chmod(o,448));const n=`${i}.tmp-${process.pid}-${Date.now()}`;try{await c.writeFile(n,e,{mode:384,flag:"wx"}),"win32"!==process.platform&&await c.chmod(n,384),await c.rename(n,i),"win32"!==process.platform&&await c.chmod(i,384)}catch(e){throw await c.rm(n,{force:!0}).catch(()=>{}),e}return i}async remove(){await c.rm(this.getPath(),{force:!0})}};u=n([(0,p.Injectable)({providedIn:"root"}),l(0,(0,p.Inject)(h.FKNATPlatformService)),a("design:paramtypes",[h.FKNATPlatformService])],u),t.BillingService=u},372(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&i(t,e,r);return o(t,e),t},a=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.HelperService=void 0;const l=r(398),c=r(335),d=s(r(524)),p=s(r(697)),h=r(976),u=r(665),f=r(897),g=r(799),m=r(95),T={debug:()=>{},info:()=>{},warn:()=>{},error:()=>{}};let b=class{constructor(e,t,r,i){this.platform=e,this.tabbyConfig=t,this.i18n=r,this.log=i??T,this.log.debug("HelperService constructed",{platform:process.platform,arch:process.arch,helperPathOverride:this.config.helperPathOverride??null,logLevel:this.config.logLevel,connectionTimeoutSeconds:this.config.connectionTimeoutSeconds})}get config(){return{...u.defaultFKNATConfig,...this.tabbyConfig.store?.fknat??{}}}getHelperPath(){return this.platform.getHelperPath(this.config.helperPathOverride)}async assertExecutable(){const e=this.getHelperPath();let t;this.log.debug("Checking FKNAT helper",{helperPath:e});try{t=await d.stat(e)}catch(t){throw this.log.error("FKNAT helper stat failed",{helperPath:e,error:N(t)}),t}if(!t.isFile())throw this.log.error("FKNAT helper path is not a file",{helperPath:e}),new Error(this.i18n.instant("FKNAT helper path is not a file"));if(this.config.helperPathOverride&&"win32"!==process.platform&&!(73&t.mode))throw this.log.error("FKNAT helper is not executable",{helperPath:e,mode:t.mode.toString(8)}),new Error(this.i18n.instant("FKNAT helper is not executable"));if(this.config.helperPathOverride)return void this.log.info("Using overridden FKNAT helper",{helperPath:e});const r=p.basename(p.dirname(e)),i=p.join(p.dirname(e),"..","manifest.json");let o;try{o=JSON.parse(await d.readFile(i,"utf8"))}catch(t){throw this.log.error("FKNAT helper manifest read failed",{helperPath:e,manifestPath:i,error:N(t)}),t}const n=o[r];if(!n?.startsWith("sha256:"))throw this.log.error("FKNAT helper checksum is missing",{helperPath:e,manifestPath:i,platform:r}),new Error(this.i18n.instant("FKNAT helper checksum is missing for {platform}",{platform:r}));const s=(0,c.createHash)("sha256").update(await d.readFile(e)).digest("hex");if(n!==`sha256:${s}`)throw this.log.error("FKNAT helper checksum mismatch",{helperPath:e,manifestPath:i,platform:r,expected:n,actual:`sha256:${s}`}),new Error(this.i18n.instant("FKNAT helper checksum verification failed"));this.log.debug("FKNAT helper checksum verified",{helperPath:e,platform:r,checksum:n}),"win32"===process.platform||73&t.mode||(await d.chmod(e,64|t.mode),this.log.info("Restored execute permission on verified FKNAT helper",{helperPath:e}))}buildProxyCommand(e,t=("win32"===process.platform?"win32":"posix")){const r=this.getHelperPath(),i=this.platform.getDataDirectory();return(0,f.buildShellCommand)(r,["connect","--config-dir",i,"--server-id",e,"--timeout",`${this.config.connectionTimeoutSeconds}s`],t)}async version(){this.log.info("Checking FKNAT helper version");const e=await this.run(["version","--json"]);try{const t=JSON.parse(e.stdout);return this.log.info("FKNAT helper version received",t),t}catch(t){throw this.log.error("FKNAT helper returned invalid version JSON",{error:N(t),stdoutLength:e.stdout.length,stderrLength:e.stderr.length}),t}}async doctor(e){const t=["doctor","--config-dir",this.platform.getDataDirectory()];e&&t.push("--server-id",e),this.log.info("Running FKNAT helper doctor",{address:e??null,dataDirectory:this.platform.getDataDirectory()});const r=await this.run(t);try{const e=JSON.parse(r.stdout);return this.log.info("FKNAT helper doctor completed",e),e}catch(e){throw this.log.error("FKNAT helper returned invalid doctor JSON",{error:N(e),stdoutLength:r.stdout.length,stderrLength:r.stderr.length}),e}}async run(e){await this.assertExecutable();const t=this.platform.getDataDirectory();await d.mkdir(t,{recursive:!0,mode:448});const r=this.getHelperPath();var i;const o=1e3*(i=this.config.connectionTimeoutSeconds,Number.isFinite(i)?Math.min(300,Math.max(5,Math.ceil(i))):30)+5e3,n=e[0]??"<empty>";return this.log.debug("Starting FKNAT helper command",{helperPath:r,command:n,args:A(e),cwd:t,timeoutMs:o,logLevel:this.config.logLevel}),new Promise((i,s)=>{const a=Date.now();let c,d=!1,p=!1;const h=(0,l.spawn)(r,e,{cwd:t,env:{...process.env,FKNAT_TABBY_LOG_LEVEL:this.config.logLevel},stdio:["ignore","pipe","pipe"]});let u="",f="";h.stdout.on("data",e=>{u+=e.toString()}),h.stderr.on("data",e=>{f+=e.toString()});const m=()=>{c&&clearTimeout(c),c=void 0};h.once("error",e=>{m(),d||(d=!0,this.log.error("FKNAT helper spawn failed",{helperPath:r,command:n,error:N(e),elapsedMs:Date.now()-a}),s(e))}),h.once("close",(e,t)=>{m();const o=Date.now()-a;if(this.log.debug("FKNAT helper process closed",{helperPath:r,command:n,code:e,signal:t,timedOut:p,elapsedMs:o,stdoutLength:u.length,stderrLength:f.length}),!d){if(d=!0,0!==e){const i=(0,g.parseHelperError)(f),a=i?this.i18n.instant(i.message):this.i18n.instant("FKNAT helper exited with status {status}",{status:e});return this.log.error("FKNAT helper exited unsuccessfully",{helperPath:r,command:n,code:e,signal:t,elapsedMs:o,parsedCode:i?.code??null,stderr:v(f)}),void s(new Error(a))}this.log.info("FKNAT helper command completed",{command:n,elapsedMs:o,stdoutLength:u.length,stderrLength:f.length,stderr:f?v(f):void 0}),i({stdout:u,stderr:f})}}),c=setTimeout(()=>{d||(p=!0,this.log.error("FKNAT helper command timed out",{helperPath:r,command:n,timeoutMs:o,elapsedMs:Date.now()-a}),h.kill(),d=!0,s(new Error(this.i18n.instant("FKNAT helper command timed out after {seconds}s",{seconds:Math.ceil(o/1e3)}))))},o)})}};function A(e){return e.map((t,r)=>"--server-id"===e[r-1]?`${t.slice(0,8)}…${t.slice(-8)}`:t)}function v(e,t=2048){return e.length<=t?e:`${e.slice(0,t)}…[truncated ${e.length-t} bytes]`}function N(e){return e instanceof Error?`${e.name}: ${e.message}`:String(e)}b=n([(0,h.Injectable)(),a("design:paramtypes",[m.FKNATPlatformService,Function,Object,Function])],b),t.HelperService=b},640(e,t,r){var i,o=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},s=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATTranslationService=void 0;const a=r(976),l=r(414),c=r(438);let d=class{constructor(e,t){this.locale=e,this.translate=t,this.localeSubscription=this.locale.localeChanged$.subscribe(e=>this.install(e));const r=this.locale.getLocale();this.translate.getLangs().includes(r)&&this.install(r)}instant(e,t){return this.translate.instant(e,t)}ngOnDestroy(){this.localeSubscription.unsubscribe()}install(e){const t=c.FKNAT_TRANSLATIONS[e];t&&this.translate.setTranslation(e,t,!0)}};d=o([(0,a.Injectable)(),s(0,(0,a.Inject)(l.LocaleService)),s(1,(0,a.Inject)(l.TranslateService)),n("design:paramtypes",[l.LocaleService,"function"==typeof(i=void 0!==l.TranslateService&&l.TranslateService)?i:Object])],d),t.FKNATTranslationService=d},95(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&i(t,e,r);return o(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATPlatformService=t.bundledHelperPath=t.currentHelperPlatform=void 0;const a=s(r(697)),l=s(r(228)),c=r(976);function d(e=process.platform,t=process.arch){const r=new Set(["win32-x64","win32-arm64","linux-x64","linux-arm64","darwin-x64","darwin-arm64"]),i=`${e}-${"ia32"===t?"x86":t}`;if("win32-x86"===i)throw new Error("FKNAT helper does not support 32-bit Windows");if(!r.has(i))throw new Error(`Unsupported FKNAT helper platform: ${i}`);return i}function p(e,t=process.platform,r=process.arch){const i="dist"===a.basename(e)?a.dirname(e):a.resolve(e,"..",".."),o=d(t,r),n="win32"===t?".exe":"";return a.join(i,"bin",o,`fknat-tabby-helper${n}`)}t.currentHelperPlatform=d,t.bundledHelperPath=p;let h=class{getDataDirectory(){const e=l.homedir()||process.env.HOME||process.env.USERPROFILE||".";return"win32"===process.platform?a.join(process.env.APPDATA??a.join(e,"AppData","Roaming"),"tabby-fknat"):"darwin"===process.platform?a.join(e,"Library","Application Support","tabby-fknat"):a.join(process.env.XDG_CONFIG_HOME??a.join(e,".config"),"tabby-fknat")}getHelperPath(e){return e?.trim()?e.trim():p(__dirname)}getBillingPath(){return a.join(this.getDataDirectory(),"secrets","billing.json")}getIdentityPath(){return a.join(this.getDataDirectory(),"client.identity")}};h=n([(0,c.Injectable)({providedIn:"root"})],h),t.FKNATPlatformService=h},922(e,t){function r(e){const r=e.trim();if(!t.$.test(r))throw new Error("FKNAT Address must be exactly 64 hexadecimal characters");return r.toLowerCase()}t.isFKNATAddress=t.normalizeFKNATAddress=t.$=void 0,t.$=/^[0-9a-fA-F]{64}$/,t.normalizeFKNATAddress=r,t.isFKNATAddress=function(e){try{return r(e),!0}catch{return!1}}},799(e,t){t.parseHelperError=void 0;const r={INVALID_ARGUMENT:"The FKNAT helper received invalid connection settings.",BILLING_KEY_INVALID:"Your FKNAT billing authorization is invalid. Import a new billing key.",IDENTITY_ERROR:"The local FKNAT client identity could not be loaded or created.",ADDRESS_INVALID:"The FKNAT Address is invalid.",CA_ADMISSION_FAILED:"FKNAT network admission failed.",DISCOVERY_FAILED:"FKNAT network discovery failed.",SERVER_OFFLINE:"The FKNAT target is currently offline.",ROUTE_REJECTED:"The target is online but does not expose the default FKNAT SSH route.",ROUTE_UNAVAILABLE:"The FKNAT target is reachable, but its SSH route is not available.",NETWORK_FAILED:"The FKNAT network connection failed.",BILLING_FAILED:"FKNAT billing authorization failed.",INTERNAL_ERROR:"The FKNAT helper failed unexpectedly."};t.parseHelperError=function(e){const t=/(?:^|\n)FKNAT_ERROR code=([A-Z_]+)/.exec(e);if(!t||!(t[1]in r))return null;const i=t[1];return{code:i,message:r[i]}}},897(e,t){t.buildShellCommand=void 0,t.buildShellCommand=function(e,t,r){return[e,...t].map(e=>function(e,t){return"win32"===t?function(e){return`"${e.replace(/"/g,'""')}"`}(e):function(e){return 0===e.length?"''":`'${e.replace(/'/g,"'\"'\"'")}'`}(e)}(e,r)).join(" ")}},282(e){e.exports=require("@angular/common")},976(e){e.exports=require("@angular/core")},98(e){e.exports=require("@angular/forms")},398(e){e.exports=require("node:child_process")},335(e){e.exports=require("node:crypto")},524(e){e.exports=require("node:fs/promises")},228(e){e.exports=require("node:os")},697(e){e.exports=require("node:path")},414(e){e.exports=require("tabby-core")},120(e){e.exports=require("tabby-settings")},265(e){e.exports=require("tabby-terminal")}};const t={};let r=function r(i){const o=t[i];if(void 0!==o)return o.exports;const n=t[i]={exports:{}};return e[i].call(n.exports,n,n.exports,r),n.exports}(156);module.exports=r})();
1
+ (()=>{"use strict";var e={966(e,t,r){var i=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},o=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATProfileSettingsComponent=void 0;const s=r(976),a=r(438),l=r(922);let c=class{constructor(e){this.i18n=e,this.addressError=""}get privateKey(){return this.profile.options.privateKeys?.[0]??""}set privateKey(e){this.profile.options.privateKeys=e.trim()?[e.trim()]:[]}save(){(0,l.isFKNATAddress)(this.profile.options.address)?(this.profile.options.address=(0,l.normalizeFKNATAddress)(this.profile.options.address),this.profile.options.port=this.profile.options.port||22,this.profile.options.user=this.profile.options.user?.trim()||"root",this.profile.options.fknatProfileVersion=1):this.addressError=this.i18n.instant("Address must be exactly 64 hexadecimal characters.")}};c=i([(0,s.Component)({selector:"fknat-profile-settings",template:'\n <div class="form-group">\n <label translate>FKNAT Address</label>\n <input class="form-control" [(ngModel)]="profile.options.address" [placeholder]="\'64-character FKNAT Address\' | translate">\n <small class="form-text text-muted" *ngIf="addressError">{{ addressError }}</small>\n </div>\n <div class="form-group">\n <label translate>SSH port</label>\n <input class="form-control" type="number" [(ngModel)]="profile.options.port" min="1" max="65535">\n </div>\n <div class="form-group">\n <label translate>SSH username</label>\n <input class="form-control" [(ngModel)]="profile.options.user" autocomplete="username">\n </div>\n <div class="form-group">\n <label translate>Authentication</label>\n <select class="form-control" [(ngModel)]="profile.options.auth">\n <option [ngValue]="null" translate>Automatic</option>\n <option value="password" translate>Password</option>\n <option value="publicKey" translate>Public key</option>\n <option value="agent" translate>SSH agent</option>\n <option value="keyboardInteractive" translate>Keyboard interactive</option>\n </select>\n </div>\n <small class="form-text text-muted" *ngIf="profile.options.auth === \'password\'" translate>\n Tabby will prompt for the SSH password and may save it in its native keychain.\n </small>\n <div class="form-group" *ngIf="profile.options.auth === \'publicKey\'">\n <label translate>Private key</label>\n <input class="form-control" [(ngModel)]="privateKey" placeholder="~/.ssh/id_ed25519">\n </div>\n <p class="text-muted" translate>FKNAT route 0 and the helper transport are managed automatically.</p>\n '}),n(0,(0,s.Inject)(a.FKNAT_TRANSLATION_SERVICE)),o("design:paramtypes",[Object])],c),t.FKNATProfileSettingsComponent=c},786(e,t,r){var i=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},o=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATSettingsTabProvider=t.FKNATSettingsTabComponent=void 0;const s=r(976),a=r(414),l=r(120),c=r(438),d=r(551),p=r(372);let h=class{constructor(e,t,r,i){this.billingService=e,this.helper=t,this.i18n=r,this.configured=!1,this.busy=!1,this.message="",this.log=i.create("tabby-fknat.settings"),this.log.debug("FKNAT settings component constructed"),this.refresh().catch(e=>{this.log.error("FKNAT settings refresh failed",{error:u(e)}),this.message=this.i18n.instant("FKNAT diagnostics failed.")})}async refresh(){this.log.debug("Refreshing FKNAT settings state"),this.configured=await this.billingService.isConfigured(),this.log.info("FKNAT settings state refreshed",{configured:this.configured})}async importBilling(e){const t=e.files?.[0];if(t){this.busy=!0,this.message="",this.log.info("Importing FKNAT billing key",{size:t.size,name:t.name});try{const e=await this.billingService.importFromBytes(new Uint8Array(await t.arrayBuffer()));this.message=this.i18n.instant("Billing key imported."),this.log.info("FKNAT billing key imported",{destination:e,size:t.size}),await this.refresh()}catch(e){this.log.error("FKNAT billing key import failed",{error:u(e)}),this.message=this.i18n.instant(e instanceof Error?e.message:"Billing key import failed.")}finally{this.busy=!1,e.value=""}}}async runDoctor(){this.busy=!0,this.message="",this.log.info("FKNAT settings diagnostics requested");try{const e=await this.helper.doctor();this.log.info("FKNAT settings diagnostics completed",e),this.message=e.ok?this.i18n.instant("FKNAT network: {status}",{status:e.network_status??this.i18n.instant("ready")}):this.i18n.instant("FKNAT diagnostics failed.")}catch(e){this.log.error("FKNAT settings diagnostics failed",{error:u(e)}),this.message=this.i18n.instant(e instanceof Error?e.message:"FKNAT diagnostics failed.")}finally{this.busy=!1}}};function u(e){return e instanceof Error?`${e.name}: ${e.message}`:String(e)}h=i([(0,s.Component)({selector:"fknat-settings-tab",template:'\n <h3 translate>FKNAT Network</h3>\n <p *ngIf="configured" class="text-success" translate>Billing authorization: Ready</p>\n <p *ngIf="!configured" class="text-warning" translate>FKNAT billing key is not configured.</p>\n <input #billingInput type="file" accept="application/json,.json" hidden (change)="importBilling(billingInput)">\n <button class="btn btn-primary" type="button" (click)="billingInput.click()">\n {{ (configured ? \'Replace billing key\' : \'Import billing key\') | translate }}\n </button>\n <button class="btn btn-secondary ml-2" type="button" (click)="runDoctor()" [disabled]="busy" translate>Run diagnostics</button>\n <p *ngIf="message" class="mt-3">{{ message }}</p>\n <small class="text-muted" translate>The private key is stored locally and is not included in SSH profiles.</small>\n '}),n(0,(0,s.Inject)(d.BillingService)),n(1,(0,s.Inject)(p.HelperService)),n(2,(0,s.Inject)(c.FKNAT_TRANSLATION_SERVICE)),n(3,(0,s.Inject)(a.LogService)),o("design:paramtypes",[d.BillingService,p.HelperService,Object,a.LogService])],h),t.FKNATSettingsTabComponent=h;let f=class extends l.SettingsTabProvider{constructor(){super(...arguments),this.id="fknat",this.icon="fas fa-network-wired",this.title="FKNAT",this.weight=20,this.prioritized=!1}getComponentType(){return h}};f=i([(0,s.Injectable)()],f),t.FKNATSettingsTabProvider=f},438(e,t){t.FKNAT_TRANSLATIONS=t.FKNAT_TRANSLATION_SERVICE=void 0,t.FKNAT_TRANSLATION_SERVICE="tabby-fknat.translation-service",t.FKNAT_TRANSLATIONS={"zh-CN":{"FKNAT Network":"FKNAT 网络","Billing authorization: Ready":"扣费授权:已就绪","FKNAT billing key is not configured.":"尚未配置 FKNAT 扣费密钥。","Replace billing key":"替换扣费密钥","Import billing key":"导入扣费密钥","Run diagnostics":"运行诊断","The private key is stored locally and is not included in SSH profiles.":"私钥仅保存在本机,不会写入 SSH 配置。","Billing key imported.":"扣费密钥已导入。","Billing key import failed.":"扣费密钥导入失败。","FKNAT network: {status}":"FKNAT 网络:{status}",ready:"已就绪","FKNAT diagnostics failed.":"FKNAT 诊断失败。","FKNAT Address":"FKNAT 地址","64-character FKNAT Address":"64 位 FKNAT 地址","SSH port":"SSH 端口","SSH username":"SSH 用户名",Authentication:"身份验证",Automatic:"自动",Password:"密码","Public key":"公钥","SSH agent":"SSH Agent","Keyboard interactive":"键盘交互","Tabby will prompt for the SSH password and may save it in its native keychain.":"Tabby 将提示输入 SSH 密码,并可能将其保存在系统钥匙串中。","Private key":"私钥","FKNAT route 0 and the helper transport are managed automatically.":"FKNAT 路由 0 和 helper 传输由插件自动管理。","Address must be exactly 64 hexadecimal characters.":"地址必须正好包含 64 个十六进制字符。","FKNAT SSH connection":"FKNAT SSH 连接","The native Tabby SSH plugin is not available. Enable it and restart Tabby.":"Tabby 原生 SSH 插件不可用,请启用该插件并重启 Tabby。","Unsupported FKNAT helper protocol version {version}":"不支持 FKNAT helper 协议版本 {version}","Billing key file must be between 1 and 65536 bytes":"扣费密钥文件大小必须介于 1 到 65536 字节之间","Billing key file must contain valid JSON":"扣费密钥文件必须包含有效的 JSON","Billing key JSON must be an object":"扣费密钥 JSON 必须是一个对象","FKNAT helper path is not a file":"FKNAT helper 路径不是文件","FKNAT helper is not executable":"FKNAT helper 不可执行","FKNAT helper checksum is missing for {platform}":"缺少 {platform} 平台的 FKNAT helper 校验和","FKNAT helper checksum verification failed":"FKNAT helper 校验和验证失败","FKNAT helper exited with status {status}":"FKNAT helper 已退出,状态码为 {status}","FKNAT helper command timed out after {seconds}s":"FKNAT helper 在 {seconds} 秒后超时","FKNAT helper does not support 32-bit Windows":"FKNAT helper 不支持 32 位 Windows","Unsupported FKNAT helper platform: {platform}":"不支持的 FKNAT helper 平台:{platform}","The FKNAT helper received invalid connection settings.":"FKNAT helper 收到了无效的连接设置。","Your FKNAT billing authorization is invalid. Import a new billing key.":"FKNAT 扣费授权无效,请导入新的扣费密钥。","The local FKNAT client identity could not be loaded or created.":"无法加载或创建本地 FKNAT 客户端身份。","The FKNAT Address is invalid.":"FKNAT 地址无效。","FKNAT network admission failed.":"FKNAT 网络准入失败。","FKNAT network discovery failed.":"FKNAT 网络发现失败。","The FKNAT target is currently offline.":"FKNAT 目标当前离线。","The target is online but does not expose the default FKNAT SSH route.":"目标在线,但未开放默认的 FKNAT SSH 路由。","The FKNAT target is reachable, but its SSH route is not available.":"FKNAT 目标可达,但 SSH 路由当前不可用。","The FKNAT network connection failed.":"FKNAT 网络连接失败。","FKNAT billing authorization failed.":"FKNAT 扣费授权失败。","The FKNAT helper failed unexpectedly.":"FKNAT helper 意外失败。"},"zh-TW":{"FKNAT Network":"FKNAT 網路","Billing authorization: Ready":"計費授權:已就緒","FKNAT billing key is not configured.":"尚未設定 FKNAT 計費金鑰。","Replace billing key":"替換計費金鑰","Import billing key":"匯入計費金鑰","Run diagnostics":"執行診斷","The private key is stored locally and is not included in SSH profiles.":"私鑰僅儲存在本機,不會寫入 SSH 設定檔。","Billing key imported.":"計費金鑰已匯入。","Billing key import failed.":"計費金鑰匯入失敗。","FKNAT network: {status}":"FKNAT 網路:{status}",ready:"已就緒","FKNAT diagnostics failed.":"FKNAT 診斷失敗。","FKNAT Address":"FKNAT 位址","64-character FKNAT Address":"64 位 FKNAT 位址","SSH port":"SSH 連接埠","SSH username":"SSH 使用者名稱",Authentication:"身分驗證",Automatic:"自動",Password:"密碼","Public key":"公開金鑰","SSH agent":"SSH Agent","Keyboard interactive":"鍵盤互動","Tabby will prompt for the SSH password and may save it in its native keychain.":"Tabby 將提示輸入 SSH 密碼,並可能將其儲存在系統鑰匙圈中。","Private key":"私鑰","FKNAT route 0 and the helper transport are managed automatically.":"FKNAT 路由 0 和 helper 傳輸由外掛程式自動管理。","Address must be exactly 64 hexadecimal characters.":"位址必須正好包含 64 個十六進位字元。","FKNAT SSH connection":"FKNAT SSH 連線","The native Tabby SSH plugin is not available. Enable it and restart Tabby.":"Tabby 原生 SSH 外掛程式無法使用,請啟用該外掛程式並重新啟動 Tabby。","Unsupported FKNAT helper protocol version {version}":"不支援 FKNAT helper 通訊協定版本 {version}","Billing key file must be between 1 and 65536 bytes":"計費金鑰檔案大小必須介於 1 到 65536 位元組之間","Billing key file must contain valid JSON":"計費金鑰檔案必須包含有效的 JSON","Billing key JSON must be an object":"計費金鑰 JSON 必須是物件","FKNAT helper path is not a file":"FKNAT helper 路徑不是檔案","FKNAT helper is not executable":"FKNAT helper 無法執行","FKNAT helper checksum is missing for {platform}":"缺少 {platform} 平台的 FKNAT helper 檢查碼","FKNAT helper checksum verification failed":"FKNAT helper 檢查碼驗證失敗","FKNAT helper exited with status {status}":"FKNAT helper 已結束,狀態碼為 {status}","FKNAT helper command timed out after {seconds}s":"FKNAT helper 在 {seconds} 秒後逾時","FKNAT helper does not support 32-bit Windows":"FKNAT helper 不支援 32 位元 Windows","Unsupported FKNAT helper platform: {platform}":"不支援的 FKNAT helper 平台:{platform}","The FKNAT helper received invalid connection settings.":"FKNAT helper 收到無效的連線設定。","Your FKNAT billing authorization is invalid. Import a new billing key.":"FKNAT 計費授權無效,請匯入新的計費金鑰。","The local FKNAT client identity could not be loaded or created.":"無法載入或建立本機 FKNAT 用戶端身分。","The FKNAT Address is invalid.":"FKNAT 位址無效。","FKNAT network admission failed.":"FKNAT 網路准入失敗。","FKNAT network discovery failed.":"FKNAT 網路探索失敗。","The FKNAT target is currently offline.":"FKNAT 目標目前離線。","The target is online but does not expose the default FKNAT SSH route.":"目標在線上,但未開放預設的 FKNAT SSH 路由。","The FKNAT target is reachable, but its SSH route is not available.":"FKNAT 目標可達,但 SSH 路由目前無法使用。","The FKNAT network connection failed.":"FKNAT 網路連線失敗。","FKNAT billing authorization failed.":"FKNAT 計費授權失敗。","The FKNAT helper failed unexpectedly.":"FKNAT helper 發生未預期的錯誤。"}}},156(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||i(t,e,r)},n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var s=r(858);Object.defineProperty(t,"default",{enumerable:!0,get:function(){return n(s).default}}),o(r(490),t),o(r(372),t),o(r(551),t)},665(e,t){t.defaultFKNATConfig=void 0,t.defaultFKNATConfig={configVersion:1,helperPathOverride:null,logLevel:"warn",connectionTimeoutSeconds:30}},490(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.parseFKNATQuickConnect=t.toNativeSSHProfile=t.TABBY_SSH_DEFAULT_ALGORITHMS=void 0;const i=r(922);t.TABBY_SSH_DEFAULT_ALGORITHMS={kex:["mlkem768x25519-sha256","curve25519-sha256","curve25519-sha256@libssh.org","diffie-hellman-group16-sha512","diffie-hellman-group14-sha256","ext-info-c","ext-info-s","kex-strict-c-v00@openssh.com","kex-strict-s-v00@openssh.com"],serverHostKey:["ssh-ed25519","ecdsa-sha2-nistp256","ecdsa-sha2-nistp521","rsa-sha2-256","rsa-sha2-512","ssh-rsa"],cipher:["chacha20-poly1305@openssh.com","aes256-gcm@openssh.com","aes256-ctr","aes192-ctr","aes128-ctr"],hmac:["hmac-sha2-512-etm@openssh.com","hmac-sha2-256-etm@openssh.com","hmac-sha2-512","hmac-sha2-256","hmac-sha1-etm@openssh.com","hmac-sha1"],compression:["none"]},t.toNativeSSHProfile=function(e,r){const o=(0,i.normalizeFKNATAddress)(e.options.address);return{...e,type:"ssh",clearServiceMessagesOnConnect:e.clearServiceMessagesOnConnect??!0,options:{host:o,port:e.options.port??22,user:e.options.user||"root",auth:e.options.auth??null,password:e.options.password,privateKeys:e.options.privateKeys??[],keepaliveInterval:e.options.keepaliveInterval??5e3,keepaliveCountMax:e.options.keepaliveCountMax??10,readyTimeout:e.options.readyTimeout,x11:e.options.x11??!1,skipBanner:e.options.skipBanner??!1,agentForward:e.options.agentForward??!1,warnOnClose:e.options.warnOnClose,algorithms:(n=e.options.algorithms,Object.fromEntries(Object.entries(t.TABBY_SSH_DEFAULT_ALGORITHMS).map(([e,t])=>[e,[...n?.[e]??t]]))),proxyCommand:r,forwardedPorts:e.options.forwardedPorts??[],scripts:e.options.scripts??[],reuseSession:e.options.reuseSession??!0,input:e.options.input??{backspace:"backspace"}}};var n},t.parseFKNATQuickConnect=function(e){const t=/^fknat:\/\/(?:([^@/?#]+)@)?([^/?#]+)\/?$/i.exec(e.trim());if(!t)return null;let r,o;try{r=(0,i.normalizeFKNATAddress)(t[2])}catch{return null}try{o=t[1]?decodeURIComponent(t[1]):"root"}catch{return null}return!o||/[\x00-\x1f\x7f]/.test(o)?null:{type:"fknat-ssh",name:`${o}@${r.slice(0,12)}...`,options:{address:r,port:22,user:o,auth:null,privateKeys:[],fknatProfileVersion:1}}}},858(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&i(t,e,r);return o(t,e),t},a=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},l=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}},c=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const d=r(976),p=r(282),h=r(98),u=s(r(414)),f=s(r(120)),g=c(r(265)),m=r(966),T=r(786),b=r(438),A=r(665),v=r(459),N=r(551),y=r(372),F=r(640),K=r(95);class S extends u.ConfigProvider{constructor(){super(...arguments),this.defaults={fknat:A.defaultFKNATConfig},this.platformDefaults={}}}let w=class{constructor(e,t){this.translations=e,t.create("tabby-fknat.module").info("FKNAT Tabby plugin module loaded",{pluginVersion:"0.1.3",moduleDirectory:__dirname,platform:process.platform,arch:process.arch})}};w=n([(0,d.NgModule)({imports:[p.CommonModule,h.FormsModule,u.default,f.default,g.default],providers:[K.FKNATPlatformService,N.BillingService,F.FKNATTranslationService,{provide:b.FKNAT_TRANSLATION_SERVICE,useExisting:F.FKNATTranslationService},{provide:y.HelperService,useFactory:(e,t,r,i)=>new y.HelperService(e,t,r,i.create("tabby-fknat.helper")),deps:[K.FKNATPlatformService,u.ConfigService,b.FKNAT_TRANSLATION_SERVICE,u.LogService]},{provide:u.ConfigProvider,useClass:S,multi:!0},{provide:u.ProfileProvider,useExisting:v.FKNATProfileProvider,multi:!0},{provide:v.FKNATProfileProvider,useClass:v.FKNATProfileProvider},{provide:f.SettingsTabProvider,useClass:T.FKNATSettingsTabProvider,multi:!0}],declarations:[m.FKNATProfileSettingsComponent,T.FKNATSettingsTabComponent]}),l(0,(0,d.Inject)(F.FKNATTranslationService)),l(1,(0,d.Inject)(u.LogService)),a("design:paramtypes",[F.FKNATTranslationService,u.LogService])],w),t.default=w},459(e,t,r){var i=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},o=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},n=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATProfileProvider=void 0;const s=r(976),a=r(414),l=r(966),c=r(438),d=r(490),p=r(372);let h=class extends a.QuickConnectProfileProvider{constructor(e,t,r,i){super(),this.helper=e,this.i18n=t,this.injector=i,this.id="fknat-ssh",this.name="FKNAT SSH",this.settingsComponent=l.FKNATProfileSettingsComponent,this.configDefaults={options:{address:"",port:22,user:"root",auth:null,password:null,privateKeys:[],agentForward:!1,forwardedPorts:[],fknatProfileVersion:1},clearServiceMessagesOnConnect:!0},this.log=r.create("tabby-fknat.profile"),this.log.debug("FKNAT profile provider constructed")}async getBuiltinProfiles(){return[{id:"fknat-ssh:template",type:this.id,name:this.i18n.instant("FKNAT SSH connection"),icon:"fas fa-network-wired",isBuiltin:!0,isTemplate:!0,weight:-1,options:{address:"",port:22,user:"root",auth:null,privateKeys:[],fknatProfileVersion:1}}]}async getNewTabParameters(e){this.log.info("Preparing FKNAT SSH tab",{address:u(e.options.address),user:e.options.user||"root",port:e.options.port,auth:e.options.auth??"automatic",configuredPrivateKeyCount:e.options.privateKeys?.length??0,privateKeySource:e.options.auth&&"publicKey"!==e.options.auth?"disabled by authentication mode":e.options.privateKeys?.length?"configured":"automatic discovery"}),await this.helper.assertExecutable();const t=await this.helper.version();if(this.log.debug("FKNAT helper protocol checked",t),1!==t.protocol_version)throw new Error(this.i18n.instant("Unsupported FKNAT helper protocol version {version}",{version:t.protocol_version}));const r=(0,d.toNativeSSHProfile)(e,this.helper.buildProxyCommand(e.options.address)),i=this.injector.get(a.ProfilesService),o=i.providerForProfile(r);if(!o)throw this.log.error("Tabby native SSH profile provider is unavailable"),new Error(this.i18n.instant("The native Tabby SSH plugin is not available. Enable it and restart Tabby."));const n=await o.getNewTabParameters(i.getConfigProxyForProfile(r));return this.log.info("FKNAT SSH tab parameters ready",{address:u(e.options.address),helperVersion:t.helper_version,protocolVersion:t.protocol_version,sshProvider:o.constructor.name,sshComponent:n.type.name,auth:r.options.auth??"automatic",configuredPrivateKeyCount:r.options.privateKeys?.length??0}),n}getSuggestedName(e){return`${e.options?.user||"root"}@${e.options?.address||"FKNAT"}`}getDescription(e){return e.options?.address??""}quickConnect(e){return(0,d.parseFKNATQuickConnect)(e)}intoQuickConnectString(e){return`fknat://${e.options.user||"root"}@${e.options.address}`}};function u(e){return e?e.length<=16?e:`${e.slice(0,8)}…${e.slice(-8)}`:"<empty>"}h=i([(0,s.Injectable)({providedIn:"root"}),n(0,(0,s.Inject)(p.HelperService)),n(1,(0,s.Inject)(c.FKNAT_TRANSLATION_SERVICE)),n(2,(0,s.Inject)(a.LogService)),n(3,(0,s.Inject)(s.Injector)),o("design:paramtypes",[p.HelperService,Object,a.LogService,s.Injector])],h),t.FKNATProfileProvider=h},551(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&i(t,e,r);return o(t,e),t},a=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},l=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BillingService=void 0;const c=s(r(524)),d=s(r(697)),p=r(976),h=r(95);let u=class{constructor(e){this.platform=e}getPath(){return this.platform.getBillingPath()}async isConfigured(){try{const e=await c.stat(this.getPath());return e.isFile()&&e.size>0&&e.size<=65536}catch{return!1}}async importFromBytes(e){if(0===e.length||e.length>65536)throw new Error("Billing key file must be between 1 and 65536 bytes");const t=(new TextDecoder).decode(e);let r;try{r=JSON.parse(t)}catch{throw new Error("Billing key file must contain valid JSON")}if(!r||"object"!=typeof r||Array.isArray(r))throw new Error("Billing key JSON must be an object");const i=this.getPath(),o=d.dirname(i);await c.mkdir(o,{recursive:!0,mode:448}),"win32"!==process.platform&&(await c.chmod(d.dirname(o),448),await c.chmod(o,448));const n=`${i}.tmp-${process.pid}-${Date.now()}`;try{await c.writeFile(n,e,{mode:384,flag:"wx"}),"win32"!==process.platform&&await c.chmod(n,384),await c.rename(n,i),"win32"!==process.platform&&await c.chmod(i,384)}catch(e){throw await c.rm(n,{force:!0}).catch(()=>{}),e}return i}async remove(){await c.rm(this.getPath(),{force:!0})}};u=n([(0,p.Injectable)({providedIn:"root"}),l(0,(0,p.Inject)(h.FKNATPlatformService)),a("design:paramtypes",[h.FKNATPlatformService])],u),t.BillingService=u},372(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&i(t,e,r);return o(t,e),t},a=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.HelperService=void 0;const l=r(398),c=r(335),d=s(r(524)),p=s(r(697)),h=r(976),u=r(665),f=r(897),g=r(799),m=r(95),T={debug:()=>{},info:()=>{},warn:()=>{},error:()=>{}};let b=class{constructor(e,t,r,i){this.platform=e,this.tabbyConfig=t,this.i18n=r,this.log=i??T,this.log.debug("HelperService constructed",{platform:process.platform,arch:process.arch,helperPathOverride:this.config.helperPathOverride??null,logLevel:this.config.logLevel,connectionTimeoutSeconds:this.config.connectionTimeoutSeconds})}get config(){return{...u.defaultFKNATConfig,...this.tabbyConfig.store?.fknat??{}}}getHelperPath(){return this.platform.getHelperPath(this.config.helperPathOverride)}async assertExecutable(){const e=this.getHelperPath();let t;this.log.debug("Checking FKNAT helper",{helperPath:e});try{t=await d.stat(e)}catch(t){throw this.log.error("FKNAT helper stat failed",{helperPath:e,error:N(t)}),t}if(!t.isFile())throw this.log.error("FKNAT helper path is not a file",{helperPath:e}),new Error(this.i18n.instant("FKNAT helper path is not a file"));if(this.config.helperPathOverride&&"win32"!==process.platform&&!(73&t.mode))throw this.log.error("FKNAT helper is not executable",{helperPath:e,mode:t.mode.toString(8)}),new Error(this.i18n.instant("FKNAT helper is not executable"));if(this.config.helperPathOverride)return void this.log.info("Using overridden FKNAT helper",{helperPath:e});const r=p.basename(p.dirname(e)),i=p.join(p.dirname(e),"..","manifest.json");let o;try{o=JSON.parse(await d.readFile(i,"utf8"))}catch(t){throw this.log.error("FKNAT helper manifest read failed",{helperPath:e,manifestPath:i,error:N(t)}),t}const n=o[r];if(!n?.startsWith("sha256:"))throw this.log.error("FKNAT helper checksum is missing",{helperPath:e,manifestPath:i,platform:r}),new Error(this.i18n.instant("FKNAT helper checksum is missing for {platform}",{platform:r}));const s=(0,c.createHash)("sha256").update(await d.readFile(e)).digest("hex");if(n!==`sha256:${s}`)throw this.log.error("FKNAT helper checksum mismatch",{helperPath:e,manifestPath:i,platform:r,expected:n,actual:`sha256:${s}`}),new Error(this.i18n.instant("FKNAT helper checksum verification failed"));this.log.debug("FKNAT helper checksum verified",{helperPath:e,platform:r,checksum:n}),"win32"===process.platform||73&t.mode||(await d.chmod(e,64|t.mode),this.log.info("Restored execute permission on verified FKNAT helper",{helperPath:e}))}buildProxyCommand(e,t=("win32"===process.platform?"win32":"posix")){const r=this.getHelperPath(),i=this.platform.getDataDirectory();return(0,f.buildShellCommand)(r,["connect","--config-dir",i,"--server-id",e,"--timeout",`${this.config.connectionTimeoutSeconds}s`],t)}async version(){this.log.info("Checking FKNAT helper version");const e=await this.run(["version","--json"]);try{const t=JSON.parse(e.stdout);return this.log.info("FKNAT helper version received",t),t}catch(t){throw this.log.error("FKNAT helper returned invalid version JSON",{error:N(t),stdoutLength:e.stdout.length,stderrLength:e.stderr.length}),t}}async doctor(e){const t=["doctor","--config-dir",this.platform.getDataDirectory()];e&&t.push("--server-id",e),this.log.info("Running FKNAT helper doctor",{address:e??null,dataDirectory:this.platform.getDataDirectory()});const r=await this.run(t);try{const e=JSON.parse(r.stdout);return this.log.info("FKNAT helper doctor completed",e),e}catch(e){throw this.log.error("FKNAT helper returned invalid doctor JSON",{error:N(e),stdoutLength:r.stdout.length,stderrLength:r.stderr.length}),e}}async run(e){await this.assertExecutable();const t=this.platform.getDataDirectory();await d.mkdir(t,{recursive:!0,mode:448});const r=this.getHelperPath();var i;const o=1e3*(i=this.config.connectionTimeoutSeconds,Number.isFinite(i)?Math.min(300,Math.max(5,Math.ceil(i))):30)+5e3,n=e[0]??"<empty>";return this.log.debug("Starting FKNAT helper command",{helperPath:r,command:n,args:A(e),cwd:t,timeoutMs:o,logLevel:this.config.logLevel}),new Promise((i,s)=>{const a=Date.now();let c,d=!1,p=!1;const h=(0,l.spawn)(r,e,{cwd:t,env:{...process.env,FKNAT_TABBY_LOG_LEVEL:this.config.logLevel},stdio:["ignore","pipe","pipe"]});let u="",f="";h.stdout.on("data",e=>{u+=e.toString()}),h.stderr.on("data",e=>{f+=e.toString()});const m=()=>{c&&clearTimeout(c),c=void 0};h.once("error",e=>{m(),d||(d=!0,this.log.error("FKNAT helper spawn failed",{helperPath:r,command:n,error:N(e),elapsedMs:Date.now()-a}),s(e))}),h.once("close",(e,t)=>{m();const o=Date.now()-a;if(this.log.debug("FKNAT helper process closed",{helperPath:r,command:n,code:e,signal:t,timedOut:p,elapsedMs:o,stdoutLength:u.length,stderrLength:f.length}),!d){if(d=!0,0!==e){const i=(0,g.parseHelperError)(f),a=i?this.i18n.instant(i.message):this.i18n.instant("FKNAT helper exited with status {status}",{status:e});return this.log.error("FKNAT helper exited unsuccessfully",{helperPath:r,command:n,code:e,signal:t,elapsedMs:o,parsedCode:i?.code??null,stderr:v(f)}),void s(new Error(a))}this.log.info("FKNAT helper command completed",{command:n,elapsedMs:o,stdoutLength:u.length,stderrLength:f.length,stderr:f?v(f):void 0}),i({stdout:u,stderr:f})}}),c=setTimeout(()=>{d||(p=!0,this.log.error("FKNAT helper command timed out",{helperPath:r,command:n,timeoutMs:o,elapsedMs:Date.now()-a}),h.kill(),d=!0,s(new Error(this.i18n.instant("FKNAT helper command timed out after {seconds}s",{seconds:Math.ceil(o/1e3)}))))},o)})}};function A(e){return e.map((t,r)=>"--server-id"===e[r-1]?`${t.slice(0,8)}…${t.slice(-8)}`:t)}function v(e,t=2048){return e.length<=t?e:`${e.slice(0,t)}…[truncated ${e.length-t} bytes]`}function N(e){return e instanceof Error?`${e.name}: ${e.message}`:String(e)}b=n([(0,h.Injectable)(),a("design:paramtypes",[m.FKNATPlatformService,Function,Object,Function])],b),t.HelperService=b},640(e,t,r){var i,o=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},n=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},s=this&&this.__param||function(e,t){return function(r,i){t(r,i,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATTranslationService=void 0;const a=r(976),l=r(414),c=r(438);let d=class{constructor(e,t){this.locale=e,this.translate=t,this.localeSubscription=this.locale.localeChanged$.subscribe(e=>this.install(e));const r=this.locale.getLocale();this.translate.getLangs().includes(r)&&this.install(r)}instant(e,t){return this.translate.instant(e,t)}ngOnDestroy(){this.localeSubscription.unsubscribe()}install(e){const t=c.FKNAT_TRANSLATIONS[e];t&&this.translate.setTranslation(e,t,!0)}};d=o([(0,a.Injectable)(),s(0,(0,a.Inject)(l.LocaleService)),s(1,(0,a.Inject)(l.TranslateService)),n("design:paramtypes",[l.LocaleService,"function"==typeof(i=void 0!==l.TranslateService&&l.TranslateService)?i:Object])],d),t.FKNATTranslationService=d},95(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,o)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__decorate||function(e,t,r,i){var o,n=arguments.length,s=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,r):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(n<3?o(s):n>3?o(t,r,s):o(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s},s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&i(t,e,r);return o(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.FKNATPlatformService=t.bundledHelperPath=t.currentHelperPlatform=void 0;const a=s(r(697)),l=s(r(228)),c=r(976);function d(e=process.platform,t=process.arch){const r=new Set(["win32-x64","win32-arm64","linux-x64","linux-arm64","darwin-x64","darwin-arm64"]),i=`${e}-${"ia32"===t?"x86":t}`;if("win32-x86"===i)throw new Error("FKNAT helper does not support 32-bit Windows");if(!r.has(i))throw new Error(`Unsupported FKNAT helper platform: ${i}`);return i}function p(e,t=process.platform,r=process.arch){const i="dist"===a.basename(e)?a.dirname(e):a.resolve(e,"..",".."),o=d(t,r),n="win32"===t?".exe":"";return a.join(i,"bin",o,`fknat-tabby-helper${n}`)}t.currentHelperPlatform=d,t.bundledHelperPath=p;let h=class{getDataDirectory(){const e=l.homedir()||process.env.HOME||process.env.USERPROFILE||".";return"win32"===process.platform?a.join(process.env.APPDATA??a.join(e,"AppData","Roaming"),"tabby-fknat"):"darwin"===process.platform?a.join(e,"Library","Application Support","tabby-fknat"):a.join(process.env.XDG_CONFIG_HOME??a.join(e,".config"),"tabby-fknat")}getHelperPath(e){return e?.trim()?e.trim():p(__dirname)}getBillingPath(){return a.join(this.getDataDirectory(),"secrets","billing.json")}getIdentityPath(){return a.join(this.getDataDirectory(),"client.identity")}};h=n([(0,c.Injectable)({providedIn:"root"})],h),t.FKNATPlatformService=h},922(e,t){function r(e){const r=e.trim();if(!t.$.test(r))throw new Error("FKNAT Address must be exactly 64 hexadecimal characters");return r.toLowerCase()}t.isFKNATAddress=t.normalizeFKNATAddress=t.$=void 0,t.$=/^[0-9a-fA-F]{64}$/,t.normalizeFKNATAddress=r,t.isFKNATAddress=function(e){try{return r(e),!0}catch{return!1}}},799(e,t){t.parseHelperError=void 0;const r={INVALID_ARGUMENT:"The FKNAT helper received invalid connection settings.",BILLING_KEY_INVALID:"Your FKNAT billing authorization is invalid. Import a new billing key.",IDENTITY_ERROR:"The local FKNAT client identity could not be loaded or created.",ADDRESS_INVALID:"The FKNAT Address is invalid.",CA_ADMISSION_FAILED:"FKNAT network admission failed.",DISCOVERY_FAILED:"FKNAT network discovery failed.",SERVER_OFFLINE:"The FKNAT target is currently offline.",ROUTE_REJECTED:"The target is online but does not expose the default FKNAT SSH route.",ROUTE_UNAVAILABLE:"The FKNAT target is reachable, but its SSH route is not available.",NETWORK_FAILED:"The FKNAT network connection failed.",BILLING_FAILED:"FKNAT billing authorization failed.",INTERNAL_ERROR:"The FKNAT helper failed unexpectedly."};t.parseHelperError=function(e){const t=/(?:^|\n)FKNAT_ERROR code=([A-Z_]+)/.exec(e);if(!t||!(t[1]in r))return null;const i=t[1];return{code:i,message:r[i]}}},897(e,t){t.buildShellCommand=void 0,t.buildShellCommand=function(e,t,r){return[e,...t].map(e=>function(e,t){return"win32"===t?function(e){return`"${e.replace(/"/g,'""')}"`}(e):function(e){return 0===e.length?"''":`'${e.replace(/'/g,"'\"'\"'")}'`}(e)}(e,r)).join(" ")}},282(e){e.exports=require("@angular/common")},976(e){e.exports=require("@angular/core")},98(e){e.exports=require("@angular/forms")},398(e){e.exports=require("node:child_process")},335(e){e.exports=require("node:crypto")},524(e){e.exports=require("node:fs/promises")},228(e){e.exports=require("node:os")},697(e){e.exports=require("node:path")},414(e){e.exports=require("tabby-core")},120(e){e.exports=require("tabby-settings")},265(e){e.exports=require("tabby-terminal")}};const t={};let r=function r(i){const o=t[i];if(void 0!==o)return o.exports;const n=t[i]={exports:{}};return e[i].call(n.exports,n,n.exports,r),n.exports}(156);module.exports=r})();
2
2
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tabby-fknat",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "FKNAT transport for Tabby's native SSH client",
5
5
  "author": "wangshiben",
6
6
  "license": "MIT",
@@ -28,7 +28,8 @@
28
28
  "prepack": "npm run verify && npm run build:helper",
29
29
  "test": "tsx --test src/**/*.test.ts",
30
30
  "typecheck": "tsc --noEmit",
31
- "verify": "npm run typecheck && npm test && npm run build"
31
+ "verify": "npm run typecheck && npm test && npm run build",
32
+ "verify:package": "node scripts/verify-package.mjs"
32
33
  },
33
34
  "peerDependencies": {
34
35
  "@angular/animations": "^15",
@@ -44,6 +45,20 @@
44
45
  "tabby-ssh": "*",
45
46
  "tabby-terminal": "*"
46
47
  },
48
+ "peerDependenciesMeta": {
49
+ "@angular/animations": { "optional": true },
50
+ "@angular/common": { "optional": true },
51
+ "@angular/core": { "optional": true },
52
+ "@angular/forms": { "optional": true },
53
+ "@angular/platform-browser": { "optional": true },
54
+ "@angular/platform-browser-dynamic": { "optional": true },
55
+ "@ng-bootstrap/ng-bootstrap": { "optional": true },
56
+ "rxjs": { "optional": true },
57
+ "tabby-core": { "optional": true },
58
+ "tabby-settings": { "optional": true },
59
+ "tabby-ssh": { "optional": true },
60
+ "tabby-terminal": { "optional": true }
61
+ },
47
62
  "devDependencies": {
48
63
  "@angular/animations": "^15.2.10",
49
64
  "@angular/common": "^15.2.10",