From ea4422585a08d7dbada1bdd6dbee421275914619 Mon Sep 17 00:00:00 2001 From: Matt Hill <9935159+MattDHill@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:57:23 -0600 Subject: [PATCH 1/3] chore: migrate to start-sdk 2.0 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01F5qZh4QiVySBLkV5ya7XSr --- .github/workflows/build.yml | 4 +- .github/workflows/release.yml | 3 +- .github/workflows/tagAndRelease.yml | 3 +- AGENTS.md | 18 +- CONTRIBUTING.md | 43 - Makefile | 2 +- package-lock.json | 1370 ++++++++++++++++++++++++++- package.json | 4 +- s9pk.mk | 138 --- startos/dependencies.ts | 2 +- startos/main.ts | 4 +- startos/versions/current.ts | 37 +- tsconfig.json | 11 +- 13 files changed, 1366 insertions(+), 273 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 s9pk.mk diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dafe4e5..7f7dc2d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,8 +13,6 @@ concurrency: jobs: build: if: github.event.pull_request.draft == false - uses: start9labs/shared-workflows/.github/workflows/build.yml@master - # with: - # FREE_DISK_SPACE: true + uses: Start9Labs/start-technologies/.github/workflows/build.yml@master secrets: DEV_KEY: ${{ secrets.DEV_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b83acf..6dcef4a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,9 +7,8 @@ on: jobs: release: - uses: start9labs/shared-workflows/.github/workflows/release.yml@master + uses: Start9Labs/start-technologies/.github/workflows/release.yml@master with: - # FREE_DISK_SPACE: true RELEASE_REGISTRY: ${{ vars.RELEASE_REGISTRY }} S3_S9PKS_BASE_URL: ${{ vars.S3_S9PKS_BASE_URL }} secrets: diff --git a/.github/workflows/tagAndRelease.yml b/.github/workflows/tagAndRelease.yml index 5690d6b..f000851 100644 --- a/.github/workflows/tagAndRelease.yml +++ b/.github/workflows/tagAndRelease.yml @@ -11,10 +11,9 @@ concurrency: jobs: tag: - uses: start9labs/shared-workflows/.github/workflows/tagAndRelease.yml@master + uses: Start9Labs/start-technologies/.github/workflows/tagAndRelease.yml@master with: REFERENCE_REGISTRY: ${{ vars.REFERENCE_REGISTRY }} - # FREE_DISK_SPACE: true RELEASE_REGISTRY: ${{ vars.RELEASE_REGISTRY }} S3_S9PKS_BASE_URL: ${{ vars.S3_S9PKS_BASE_URL }} secrets: diff --git a/AGENTS.md b/AGENTS.md index fe7e122..f6c5916 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,19 @@ # AGENTS.md -You are working in a StartOS service-package repository — a repo that builds a `.s9pk` for installation on StartOS. +This is a StartOS service-package repository — it builds a `.s9pk` for StartOS. -**Before doing anything in this repo, read [CONTRIBUTING.md](./CONTRIBUTING.md) and every document it links to.** That covers what this package is, how it's built, how it ships, and the conventions to follow. Do not begin work until you have read them all. +Develop it inside a StartOS packaging workspace created by `start-cli s9pk init-workspace`, +which provides the packaging guide and agent context one level up. If you're reading this in a +bare clone with no workspace, the full guide is at . + +Work this package's `TODO.md` from top to bottom. Keep `README.md` (architecture, for developers and LLMs) and `instructions.md` (end-user docs) in sync with your changes. + +## This repo + +- **Package id is `simplex`.** Ships two independent daemons in separate subcontainers: `smp` (SMP messaging server, subcontainer `smp-sub`, interface id `smp` on host `main`) and `xftp` (XFTP file-transfer server, subcontainer `xftp-sub`, interface id `xftp` on host `xftp`). Both are `api` interfaces with credentials embedded in the connection URL. +- **Tor SOCKS is read at runtime, not imported.** The optional `tor` dependency's SOCKS proxy has no StartOS host to read — when the `tor-settings` action enables it, `initServers`/`watchTorProxy` resolve Tor's container IP via `sdk.getContainerIp(effects, { packageId: 'tor' })` and write `${torIp}:9050` into `[PROXY] socks_proxy` in `smp-server.ini`. +- **INI file models use the custom parser in `startos/fileModels/ini-lib.ts`** (accepts both `=` and `:` separators, writes `=`). See `TODO.md` for the plan to retire it. + +## Inspecting a running install + +To run a command inside the service's container (read its generated config, grep app logs), use `start-cli package attach simplex -n -- `. Select the subcontainer by **name** with `-n` (the name passed to `SubContainer.of` in `main.ts` — here `smp-sub` or `xftp-sub`) or by image with `-i`. Note: `-s/--subcontainer` matches the internal **Guid**, not the name, so passing a name to `-s` fails with "no matching subcontainers". diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 8a7c6b0..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,43 +0,0 @@ -# Contributing - -## Keep these in sync - -- **[`README.md`](./README.md)** — what this package is and how it's built (image, volumes, interfaces). Technical reference for developers and AI assistants. -- **[`instructions.md`](./instructions.md)** — the user-facing instructions packed into the `.s9pk` and shown on the **Instructions** tab in StartOS, for the person running the service. -- **[`TODO.md`](./TODO.md)** — pending work on this package. - -**Read all three before starting any work.** Any code change that affects user-visible behavior must update `README.md` and `instructions.md` in the same change; add to `TODO.md` when you defer work, and remove items when complete. Content rules: [Writing READMEs](https://docs.start9.com/packaging/writing-readmes.html), [Writing Instructions](https://docs.start9.com/packaging/writing-instructions.html). - -## Environment setup - -See [Environment Setup](https://docs.start9.com/packaging/environment-setup.html) - -## Building - -```bash -npm ci # install dependencies -make # build the universal .s9pk -``` - -For a complete list of build options, see [Makefile](https://docs.start9.com/packaging/makefile.html). - -## Updating the upstream version - -1. Apply the upstream bump per [UPDATING.md](./UPDATING.md). -2. Update `version` and `releaseNotes` in `startos/versions/current.ts` — the latest version always lives in that file, so an in-place edit is all most bumps need. A new file is spun off only when the bump requires a migration — see [Versions](https://docs.start9.com/packaging/versions.html). - -## CI/CD - -Three workflows under `.github/workflows/` wrap reusable workflows in [`start9labs/shared-workflows`](https://github.com/Start9Labs/shared-workflows): - -- **`build.yml`** — on PR, builds the `.s9pk` and uploads per-arch artifacts for sideload testing. -- **`release.yml`** — on `v*` tag, builds per arch and publishes to the test registry. -- **`tagAndRelease.yml`** — on push to `master`, tags `v` and runs `release.yml`, skipping if already in production. - -Promotion to `beta` and `prod` is a separate, manual step. - -## How to contribute - -1. Fork the repository and create a branch from `master`. -2. Make your changes — including the doc updates above. -3. Open a pull request to `master`. diff --git a/Makefile b/Makefile index 10d4360..8315da3 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,3 @@ ARCHES := x86 arm # overrides to s9pk.mk must precede the include statement -include s9pk.mk +include node_modules/@start9labs/start-sdk/s9pk.mk diff --git a/package-lock.json b/package-lock.json index 43dfb04..a54b4df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,14 +6,14 @@ "": { "name": "simplex-startos", "dependencies": { - "@start9labs/start-sdk": "1.5.3", + "@start9labs/start-sdk": "2.0.1", "tldts": "^7.0.19" }, "devDependencies": { "@types/node": "^22.18.6", "@vercel/ncc": "^0.38.4", "prettier": "^3.6.2", - "typescript": "^5.9.2" + "typescript": "^6.0.3" } }, "node_modules/@iarna/toml": { @@ -62,23 +62,1320 @@ "license": "MIT" }, "node_modules/@start9labs/start-sdk": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-1.5.3.tgz", - "integrity": "sha512-OyHe9J6hMvyA5ZavcLkxdVQvZcuTH9J9kagV6NDI83eAG/YpJFIq62gP/n/2PPNdHWwNSXVQmSwnsvsV8Gyg+A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-2.0.1.tgz", + "integrity": "sha512-h0CBfS501KpQ0FX3GoYhxyt1mZYRYgvIYBygWek1kZ7Yl1LHi2uUMzp00Jln38HhB9cJWya3AM9zlGqR91uRdw==", + "bundleDependencies": [ + "@start9labs/start-core", + "eslint", + "typescript-eslint" + ], + "license": "MIT", + "dependencies": { + "@iarna/toml": "^3.0.0", + "@noble/curves": "^1.9.7", + "@noble/hashes": "^1.8.0", + "@start9labs/start-core": "file:../../shared-libs/ts-modules/start-core/dist", + "@types/ini": "^4.1.1", + "deep-equality-data-structures": "^2.0.0", + "eslint": "^9.39.4", + "fast-xml-parser": "~5.7.0", + "ini": "^5.0.0", + "isomorphic-fetch": "^3.0.0", + "mime": "^4.1.0", + "typescript-eslint": "^8.61.0", + "yaml": "^2.8.3", + "zod": "4.4.3", + "zod-deep-partial": "^1.2.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/config-array": { + "version": "0.21.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/core": { + "version": "0.17.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "inBundle": true, + "license": "Python-2.0" + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/js": { + "version": "9.39.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/object-schema": { + "version": "2.1.7", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanfs/core": { + "version": "0.19.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanfs/node": { + "version": "0.16.8", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanfs/types": { + "version": "0.15.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@types/estree": { + "version": "1.0.9", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/@types/json-schema": { + "version": "7.0.15", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/type-utils": "8.61.0", + "@typescript-eslint/utils": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.61.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/parser": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/project-service": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.61.0", + "@typescript-eslint/types": "^8.61.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/scope-manager": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/type-utils": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/utils": "8.61.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/types": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.61.0", + "@typescript-eslint/tsconfig-utils": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/utils": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/acorn": { + "version": "8.16.0", + "inBundle": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/acorn-jsx": { + "version": "5.3.2", + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/ajv": { + "version": "6.15.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/callsites": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/chalk": { + "version": "4.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/concat-map": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/cross-spawn": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/debug": { + "version": "4.4.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@start9labs/start-sdk/node_modules/deep-is": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint": { + "version": "9.39.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint-scope": { + "version": "8.4.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/espree": { + "version": "10.4.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/esquery": { + "version": "1.7.0", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/esrecurse": { + "version": "4.3.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/estraverse": { + "version": "5.3.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/esutils": { + "version": "2.0.3", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/fast-deep-equal": { + "version": "3.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/fast-levenshtein": { + "version": "2.0.6", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/fdir": { + "version": "6.5.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/@start9labs/start-sdk/node_modules/file-entry-cache": { + "version": "8.0.0", + "inBundle": true, "license": "MIT", "dependencies": { - "@iarna/toml": "^3.0.0", - "@noble/curves": "^1.9.7", - "@noble/hashes": "^1.8.0", - "@types/ini": "^4.1.1", - "deep-equality-data-structures": "^2.0.0", - "fast-xml-parser": "~5.7.0", - "ini": "^5.0.0", - "isomorphic-fetch": "^3.0.0", - "mime": "^4.1.0", - "yaml": "^2.8.3", - "zod": "4.3.6", - "zod-deep-partial": "^1.2.0" + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/flat-cache": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/flatted": { + "version": "3.4.2", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@start9labs/start-sdk/node_modules/globals": { + "version": "14.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/has-flag": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/ignore": { + "version": "5.3.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/import-fresh": { + "version": "3.3.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/is-extglob": { + "version": "2.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/is-glob": { + "version": "4.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@start9labs/start-sdk/node_modules/json-buffer": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/json-schema-traverse": { + "version": "0.4.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/keyv": { + "version": "4.5.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/levn": { + "version": "0.4.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/lodash.merge": { + "version": "4.6.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/natural-compare": { + "version": "1.4.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/optionator": { + "version": "0.9.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/p-limit": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/parent-module": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/path-exists": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/picomatch": { + "version": "4.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/prelude-ls": { + "version": "1.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/punycode": { + "version": "2.3.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/strip-json-comments": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/supports-color": { + "version": "7.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/tinyglobby": { + "version": "0.2.17", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/ts-api-utils": { + "version": "2.5.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/type-check": { + "version": "0.4.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/typescript-eslint": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.61.0", + "@typescript-eslint/parser": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/utils": "8.61.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/uri-js": { + "version": "4.4.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/word-wrap": { + "version": "1.2.5", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/yocto-queue": { + "version": "0.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@types/ini": { @@ -129,9 +1426,9 @@ } }, "node_modules/fast-xml-builder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.1.tgz", + "integrity": "sha512-tPb5TTWfgfVx5BNSi2xV0eLr89POeXXn0dXIsCJ9m1narrWxeIyx6je9d7Rce/3NyXLbvuQmLkxq+RuxMWejvw==", "funding": [ { "type": "github", @@ -244,9 +1541,9 @@ } }, "node_modules/prettier": { - "version": "3.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", - "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.4.tgz", + "integrity": "sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==", "dev": true, "license": "MIT", "bin": { @@ -275,21 +1572,21 @@ } }, "node_modules/tldts": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.4.tgz", - "integrity": "sha512-kFXFK7O4WPextIUAOk8qtnw9dxR9UIXP9CjuH1cTBVBZMDeQcUPgr/IazGiw1B0Yiw5L75gHLWeW4iD793r90g==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.6.tgz", + "integrity": "sha512-rbP0Gyx8b3Ae9yO//CU2wbSnQNoQ66m1nJdSbSHmnwKwzkkz/u8mERYU8T2rmlmy+bJvRNn84yNCW8gYqox44Q==", "license": "MIT", "dependencies": { - "tldts-core": "^7.4.4" + "tldts-core": "^7.4.6" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.4.tgz", - "integrity": "sha512-vwVLJVvvpslm7vqAH7+XNj/neA/Ynq7DT2EEcMuwc5YzN5XaMyRAqxwU+uX3azZ1FQtB2gvrvnLnAEkvYlVdfg==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.6.tgz", + "integrity": "sha512-TkQNGJIhlEphpHCjKodMTSe23egUZr/g+flI2qkLgiJ/maAzSgXypSLRTNH3nCmqgayEmtcJBiLcfODSAr1xoA==", "license": "MIT" }, "node_modules/tr46": { @@ -299,10 +1596,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -372,9 +1668,9 @@ } }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index b57a36f..4402080 100644 --- a/package.json +++ b/package.json @@ -6,14 +6,14 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "1.5.3", + "@start9labs/start-sdk": "2.0.1", "tldts": "^7.0.19" }, "devDependencies": { "@types/node": "^22.18.6", "@vercel/ncc": "^0.38.4", "prettier": "^3.6.2", - "typescript": "^5.9.2" + "typescript": "^6.0.3" }, "prettier": { "trailingComma": "all", diff --git a/s9pk.mk b/s9pk.mk deleted file mode 100644 index 4c47870..0000000 --- a/s9pk.mk +++ /dev/null @@ -1,138 +0,0 @@ -# ** Plumbing. DO NOT EDIT **. -# This file is imported by ./Makefile. Make edits there - -PACKAGE_ID := $(shell awk -F"'" '/id:/ {print $$2}' startos/manifest/index.ts) -INGREDIENTS := $(shell start-cli s9pk list-ingredients 2>/dev/null) -# Resolve the actual git dir so this works inside git worktrees, where .git -# is a file pointing at
/.git/worktrees/ rather than a directory. -GIT_DIR := $(shell git rev-parse --git-dir 2>/dev/null) -GIT_DEPS := $(if $(GIT_DIR),$(GIT_DIR)/HEAD $(GIT_DIR)/index) -ARCHES ?= x86 arm riscv -# TARGETS is the list of leaf make-targets the build matrix fans out over. -# Defaults to the arches; variant packages override (e.g. immich, ollama, vllm -# set this to a list of variant or variant-arch leaf targets). -TARGETS ?= $(ARCHES) -ifdef VARIANT -BASE_NAME := $(PACKAGE_ID)_$(VARIANT) -else -BASE_NAME := $(PACKAGE_ID) -endif - -.PHONY: all arches aarch64 x86_64 riscv64 arm arm64 x86 riscv arch/* clean install check-deps check-init package ingredients -.DELETE_ON_ERROR: -.SECONDARY: - -define SUMMARY - @manifest=$$(start-cli s9pk inspect $(1) manifest); \ - size=$$(du -h $(1) | awk '{print $$1}'); \ - title=$$(printf '%s' "$$manifest" | jq -r .title); \ - version=$$(printf '%s' "$$manifest" | jq -r .version); \ - arches=$$(printf '%s' "$$manifest" | jq -r '[.images[].arch // []] | flatten | unique | join(", ")'); \ - sdkv=$$(printf '%s' "$$manifest" | jq -r .sdkVersion); \ - gitHash=$$(printf '%s' "$$manifest" | jq -r .gitHash | sed -E 's/(.*-modified)$$/\x1b[0;31m\1\x1b[0m/'); \ - printf "\n"; \ - printf "\033[1;32m✅ Build Complete!\033[0m\n"; \ - printf "\n"; \ - printf "\033[1;37m📦 $$title\033[0m \033[36mv$$version\033[0m\n"; \ - printf "───────────────────────────────\n"; \ - printf " \033[1;36mFilename:\033[0m %s\n" "$(1)"; \ - printf " \033[1;36mSize:\033[0m %s\n" "$$size"; \ - printf " \033[1;36mArch:\033[0m %s\n" "$$arches"; \ - printf " \033[1;36mSDK:\033[0m %s\n" "$$sdkv"; \ - printf " \033[1;36mGit:\033[0m %s\n" "$$gitHash"; \ - echo "" -endef - -all: $(TARGETS) - -arches: $(ARCHES) - -# Generic make-variable introspection. Used by the release workflow to -# read $(TARGETS) and fan out one matrix runner per target. `make -s -# print-TARGETS` echoes the list with no other output. -print-%: - @echo '$($*)' - -universal: $(BASE_NAME).s9pk - $(call SUMMARY,$<) - -arch/%: $(BASE_NAME)_%.s9pk - $(call SUMMARY,$<) - -x86 x86_64: arch/x86_64 -arm arm64 aarch64: arch/aarch64 -riscv riscv64: arch/riscv64 - -$(BASE_NAME).s9pk: $(INGREDIENTS) $(GIT_DEPS) - @$(MAKE) --no-print-directory ingredients - @echo " Packing '$@'..." - start-cli s9pk pack -o $@ - -$(BASE_NAME)_%.s9pk: $(INGREDIENTS) $(GIT_DEPS) - @$(MAKE) --no-print-directory ingredients - @echo " Packing '$@'..." - start-cli s9pk pack --arch=$* -o $@ - -ingredients: $(INGREDIENTS) - @echo " Re-evaluating ingredients..." - -install: | check-deps check-init - @HOST=$$(awk -F'/' '/^host:/ {print $$3}' ~/.startos/config.yaml); \ - if [ -z "$$HOST" ]; then \ - echo "Error: You must define \"host: http://server-name.local\" in ~/.startos/config.yaml"; \ - exit 1; \ - fi; \ - S9PK=$$(ls -t *.s9pk 2>/dev/null | head -1); \ - if [ -z "$$S9PK" ]; then \ - echo "Error: No .s9pk file found. Run 'make' first."; \ - exit 1; \ - fi; \ - printf "\n🚀 Installing %s to %s ...\n" "$$S9PK" "$$HOST"; \ - start-cli package install -s "$$S9PK" - -publish: | all - @REGISTRY=$$(awk -F'/' '/^registry:/ {print $$3}' ~/.startos/config.yaml); \ - if [ -z "$$REGISTRY" ]; then \ - echo "Error: You must define \"registry: https://my-registry.tld\" in ~/.startos/config.yaml"; \ - exit 1; \ - fi; \ - S3BASE=$$(awk -F'/' '/^s9pk-s3base:/ {print $$3}' ~/.startos/config.yaml); \ - if [ -z "$$S3BASE" ]; then \ - echo "Error: You must define \"s3base: https://s9pks.my-s3-bucket.tld\" in ~/.startos/config.yaml"; \ - exit 1; \ - fi; \ - command -v s3cmd >/dev/null || \ - (echo "Error: s3cmd not found. It must be installed to publish using s3." && exit 1); \ - printf "\n🚀 Publishing to %s; indexing on %s ...\n" "$$S3BASE" "$$REGISTRY"; \ - for s9pk in *.s9pk; do \ - age=$$(( $$(date +%s) - $$(stat -c %Y "$$s9pk") )); \ - if [ "$$age" -gt 3600 ]; then \ - printf "\033[1;33m⚠️ %s is %d minutes old. Publish anyway? [y/N] \033[0m" "$$s9pk" "$$((age / 60))"; \ - read -r ans; \ - case "$$ans" in [yY]*) ;; *) echo "Skipping $$s9pk"; continue ;; esac; \ - fi; \ - start-cli s9pk publish "$$s9pk"; \ - done - -check-deps: - @command -v start-cli >/dev/null || \ - (echo "Error: start-cli not found. Please see https://docs.start9.com/latest/developer-guide/sdk/installing-the-sdk" && exit 1) - @command -v npm >/dev/null || \ - (echo "Error: npm not found. Please install Node.js and npm." && exit 1) - -check-init: - @if [ ! -f ~/.startos/developer.key.pem ]; then \ - echo "Initializing StartOS developer environment..."; \ - start-cli init-key; \ - fi - -javascript/index.js: $(shell find startos -type f) tsconfig.json node_modules - npm run check - npm run build - -node_modules: package-lock.json package.json - npm ci - -clean: - @echo "Cleaning up build artifacts..." - @rm -rf $(PACKAGE_ID).s9pk $(PACKAGE_ID)_x86_64.s9pk $(PACKAGE_ID)_aarch64.s9pk $(PACKAGE_ID)_riscv64.s9pk javascript node_modules diff --git a/startos/dependencies.ts b/startos/dependencies.ts index f49b8ac..8b6c3fd 100644 --- a/startos/dependencies.ts +++ b/startos/dependencies.ts @@ -11,7 +11,7 @@ export const setDependencies = sdk.setupDependencies(async ({ effects }) => { return { tor: { kind: 'running', - versionRange: '>=0.4.9.5:0', + versionRange: '>=0.4.9.11:2', healthChecks: [], }, } diff --git a/startos/main.ts b/startos/main.ts index 7ef82ba..a0959f7 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -31,7 +31,7 @@ export const main = sdk.setupMain(async ({ effects }) => { */ return sdk.Daemons.of(effects) .addDaemon('smp', { - subcontainer: await sdk.SubContainer.of( + subcontainer: sdk.SubContainer.of( effects, { imageId: 'smp' }, smpMounts, @@ -51,7 +51,7 @@ export const main = sdk.setupMain(async ({ effects }) => { requires: [], }) .addDaemon('xftp', { - subcontainer: await sdk.SubContainer.of( + subcontainer: sdk.SubContainer.of( effects, { imageId: 'xftp' }, xftpMounts, diff --git a/startos/versions/current.ts b/startos/versions/current.ts index 649e2d5..4b9a933 100644 --- a/startos/versions/current.ts +++ b/startos/versions/current.ts @@ -8,38 +8,13 @@ import { smpServerIni } from '../fileModels/smpServer.ini' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. export const current = VersionInfo.of({ - version: '6.5.2:0', + version: '6.5.2:1', releaseNotes: { - en_US: `Updated SimpleX (smp-server & xftp-server) to 6.5.2. - -- XFTP: backwards-compatible file header decoding. -- XFTP: server web page tweaks. - -Full changelog: https://github.com/simplex-chat/simplexmq/blob/master/CHANGELOG.md`, - es_ES: `Actualizado SimpleX (smp-server y xftp-server) a 6.5.2. - -- XFTP: decodificación de encabezados de archivo retrocompatible. -- XFTP: ajustes en la página web del servidor. - -Registro de cambios completo: https://github.com/simplex-chat/simplexmq/blob/master/CHANGELOG.md`, - de_DE: `SimpleX (smp-server & xftp-server) auf 6.5.2 aktualisiert. - -- XFTP: abwärtskompatible Dekodierung von Datei-Headern. -- XFTP: Anpassungen der Server-Webseite. - -Vollständiges Änderungsprotokoll: https://github.com/simplex-chat/simplexmq/blob/master/CHANGELOG.md`, - pl_PL: `Zaktualizowano SimpleX (smp-server i xftp-server) do 6.5.2. - -- XFTP: wstecznie zgodne dekodowanie nagłówków plików. -- XFTP: poprawki strony internetowej serwera. - -Pełna lista zmian: https://github.com/simplex-chat/simplexmq/blob/master/CHANGELOG.md`, - fr_FR: `Mise à jour de SimpleX (smp-server et xftp-server) vers 6.5.2. - -- XFTP : décodage des en-têtes de fichier rétrocompatible. -- XFTP : ajustements de la page web du serveur. - -Journal des modifications complet : https://github.com/simplex-chat/simplexmq/blob/master/CHANGELOG.md`, + en_US: 'Internal updates (start-sdk 2.0.x)', + es_ES: 'Actualizaciones internas (start-sdk 2.0.x)', + de_DE: 'Interne Aktualisierungen (start-sdk 2.0.x)', + pl_PL: 'Aktualizacje wewnętrzne (start-sdk 2.0.x)', + fr_FR: 'Mises à jour internes (start-sdk 2.0.x)', }, migrations: { up: async ({ effects }) => { diff --git a/tsconfig.json b/tsconfig.json index a2945a5..f032dc1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,4 @@ { - "include": ["startos/**/*.ts", "node_modules/**/startos"], - "compilerOptions": { - "target": "ES2018", - "module": "CommonJS", - "moduleResolution": "node", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true - } + "extends": "@start9labs/start-sdk/tsconfig.base.json", + "include": ["startos/**/*.ts", "node_modules/**/startos"] } From f8500c32ebf51db755bd4745026ce5fe5b13fe3f Mon Sep 17 00:00:00 2001 From: Matt Hill <9935159+MattDHill@users.noreply.github.com> Date: Mon, 6 Jul 2026 09:41:39 -0600 Subject: [PATCH 2/3] feat: resolve dependencies over the bridge reactively; restart only on real address changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doctrine v3 (issue start-technologies#3407): dependency addresses go through a utils.ts bridgeAddress helper chained .const() — the mapped value is the minimal bridge address, so main never restarts on dependency updates, and heals with one restart when a dependency is installed after this service, uninstalled, or changes port. Cross- package container IPs and .startos dials are gone; static config defaults become loopback placeholders; tor consumers fold the allocator-guaranteed 9050 fallback into the map. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01PpJxhk25X42WTafMdkUx6t --- AGENTS.md | 2 +- README.md | 2 +- package-lock.json | 174 +++++++++++++++++++++++++++++++++- package.json | 3 +- startos/init/watchTorProxy.ts | 17 +++- startos/utils.ts | 52 ++++++++++ startos/versions/current.ts | 15 ++- 7 files changed, 252 insertions(+), 13 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f6c5916..d403bd0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,7 +11,7 @@ Work this package's `TODO.md` from top to bottom. Keep `README.md` (architecture ## This repo - **Package id is `simplex`.** Ships two independent daemons in separate subcontainers: `smp` (SMP messaging server, subcontainer `smp-sub`, interface id `smp` on host `main`) and `xftp` (XFTP file-transfer server, subcontainer `xftp-sub`, interface id `xftp` on host `xftp`). Both are `api` interfaces with credentials embedded in the connection URL. -- **Tor SOCKS is read at runtime, not imported.** The optional `tor` dependency's SOCKS proxy has no StartOS host to read — when the `tor-settings` action enables it, `initServers`/`watchTorProxy` resolve Tor's container IP via `sdk.getContainerIp(effects, { packageId: 'tor' })` and write `${torIp}:9050` into `[PROXY] socks_proxy` in `smp-server.ini`. +- **Tor SOCKS is resolved reactively over the LXC bridge.** When the `tor-settings` action enables it, `watchTorProxy` resolves the optional `tor` dependency's SOCKS binding through the `bridgeAddress` helper in `startos/utils.ts` — `bridgeAddress(effects, { packageId: 'tor', hostId: socksHostId, internalPort: socksPort }).const()` (host id / port imported from `tor-startos/startos/utils`) — and writes the resolved `10.0.3.1:` into `[PROXY] socks_proxy` in `smp-server.ini`. No `fallbackPort` (anonymizing semantics: never dial a dead address), so the proxy line is written only once Tor's binding resolves; the `.const()` subscription heals on a late Tor install and does not restart the server on Tor updates. - **INI file models use the custom parser in `startos/fileModels/ini-lib.ts`** (accepts both `=` and `:` separators, writes `=`). See `TODO.md` for the plan to retire it. ## Inspecting a running install diff --git a/README.md b/README.md index d714d94..a631819 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ xftp://:@:5225 | Action | ID | Purpose | Inputs | Availability | |--------|----|---------|--------|--------------| -| Tor Settings | `tor-settings` | Configure whether this SMP server forwards messages to `.onion` destination servers via Tor. | `enableTorProxy: boolean` (default `false`) — when on, adds a running dependency on the Tor service and writes its container IP to `[PROXY] socks_proxy` in `smp-server.ini`; when off, strips that setting and drops the Tor dependency. | Any status | +| Tor Settings | `tor-settings` | Configure whether this SMP server forwards messages to `.onion` destination servers via Tor. | `enableTorProxy: boolean` (default `false`) — when on, adds a running dependency on the Tor service and writes Tor's SOCKS bridge address (`10.0.3.1:`, resolved via the `bridgeAddress` helper) to `[PROXY] socks_proxy` in `smp-server.ini`; when off, strips that setting and drops the Tor dependency. | Any status | The form is prepopulated from `enableTorProxy` in `store.json`; submissions merge the new value back. diff --git a/package-lock.json b/package-lock.json index a54b4df..54bca8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,8 @@ "name": "simplex-startos", "dependencies": { "@start9labs/start-sdk": "2.0.1", - "tldts": "^7.0.19" + "tldts": "^7.0.19", + "tor-startos": "github:Start9Labs/tor-startos#next" }, "devDependencies": { "@types/node": "^22.18.6", @@ -335,6 +336,163 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core": { + "inBundle": true, + "license": "MIT", + "dependencies": { + "@iarna/toml": "^3.0.0", + "@noble/curves": "^1.9.7", + "@noble/hashes": "^1.8.0", + "deep-equality-data-structures": "^2.0.0", + "isomorphic-fetch": "^3.0.0", + "mime": "^4.1.0", + "yaml": "^2.8.3", + "zod": "4.4.3", + "zod-deep-partial": "^1.4.4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/@iarna/toml": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/@noble/curves": { + "version": "1.9.7", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/@noble/hashes": { + "version": "1.8.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/deep-equality-data-structures": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "object-hash": "^3.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/isomorphic-fetch": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/mime": { + "version": "4.1.0", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "inBundle": true, + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/node-fetch": { + "version": "2.7.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/object-hash": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/tr46": { + "version": "0.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/webidl-conversions": { + "version": "3.0.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/whatwg-fetch": { + "version": "3.6.20", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/whatwg-url": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/yaml": { + "version": "2.9.0", + "inBundle": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/zod": { + "version": "4.4.3", + "inBundle": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/zod-deep-partial": { + "version": "1.4.4", + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "zod": "^4.1.13" + } + }, "node_modules/@start9labs/start-sdk/node_modules/@types/estree": { "version": "1.0.9", "inBundle": true, @@ -1556,6 +1714,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/rfc4648": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.4.tgz", + "integrity": "sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==", + "license": "MIT" + }, "node_modules/strnum": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", @@ -1589,6 +1753,14 @@ "integrity": "sha512-TkQNGJIhlEphpHCjKodMTSe23egUZr/g+flI2qkLgiJ/maAzSgXypSLRTNH3nCmqgayEmtcJBiLcfODSAr1xoA==", "license": "MIT" }, + "node_modules/tor-startos": { + "resolved": "git+ssh://git@github.com/Start9Labs/tor-startos.git#06a2ae11e7c8fb19cf405f6afbbcfaaf9fdc117d", + "dependencies": { + "@noble/curves": "*", + "@start9labs/start-sdk": "2.0.1", + "rfc4648": "^1.5.4" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", diff --git a/package.json b/package.json index 4402080..13adc09 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ }, "dependencies": { "@start9labs/start-sdk": "2.0.1", - "tldts": "^7.0.19" + "tldts": "^7.0.19", + "tor-startos": "github:Start9Labs/tor-startos#next" }, "devDependencies": { "@types/node": "^22.18.6", diff --git a/startos/init/watchTorProxy.ts b/startos/init/watchTorProxy.ts index 8028b89..da8009b 100644 --- a/startos/init/watchTorProxy.ts +++ b/startos/init/watchTorProxy.ts @@ -1,6 +1,8 @@ +import { socksHostId, socksPort } from 'tor-startos/startos/utils' import { smpServerIni } from '../fileModels/smpServer.ini' import { storeJson } from '../fileModels/store.json' import { sdk } from '../sdk' +import { bridgeAddress } from '../utils' export const watchTorProxy = sdk.setupOnInit(async (effects) => { const enableTorProxy = await storeJson @@ -16,13 +18,20 @@ export const watchTorProxy = sdk.setupOnInit(async (effects) => { return } - const torIp = await sdk - .getContainerIp(effects, { packageId: 'tor' }) - .const() + // Tor SOCKS over the bridge, no fallback: anonymizing semantics mean a dead + // address must not be dialed, so the proxy line is written only once Tor's + // binding resolves. The mapped address changes solely on Tor + // install/uninstall/port-change, so this .const() heals on late Tor install + // (one restart) and never restarts the server on Tor updates. + const socksProxy = await bridgeAddress(effects, { + packageId: 'tor', + hostId: socksHostId, + internalPort: socksPort, + }).const() await smpServerIni.merge( effects, - { PROXY: { socks_proxy: torIp ? `${torIp}:9050` : undefined } }, + { PROXY: { socks_proxy: socksProxy ?? undefined } }, { allowWriteAfterConst: true }, ) }) diff --git a/startos/utils.ts b/startos/utils.ts index a05220c..d9f0daa 100644 --- a/startos/utils.ts +++ b/startos/utils.ts @@ -1,5 +1,57 @@ +import { T } from '@start9labs/start-sdk' import { sdk } from './sdk' +/** + * Bridge address (`10.0.3.1:`) of a dependency's + * binding, as a minimal reactive value. Chain `.const()` in main (or an init + * watcher): the mapped string only changes when the address itself does, so + * the service restarts exactly on dependency install/uninstall/port-change + * and never on dependency updates. Chain `.once()` in an action context. + * `fallbackPort` keeps the value non-null while the dependency is absent — + * sanctioned only for tor's allocator-guaranteed SOCKS 9050. Drop-in for the + * planned SDK `sdk.host.getBridgeAddress` helper. + */ +export function bridgeAddress( + effects: T.Effects, + opts: { + packageId: string + hostId: string + internalPort: number + fallbackPort: number + }, +): { const(): Promise; once(): Promise } +export function bridgeAddress( + effects: T.Effects, + opts: { packageId: string; hostId: string; internalPort: number }, +): { const(): Promise; once(): Promise } +export function bridgeAddress( + effects: T.Effects, + opts: { + packageId: string + hostId: string + internalPort: number + fallbackPort?: number + }, +) { + const watchable = async () => { + const osIp = await sdk.getOsIp(effects) + return sdk.host.get( + effects, + { packageId: opts.packageId, hostId: opts.hostId }, + (host) => { + const port = + host?.bindings[opts.internalPort]?.net.assignedPort ?? + opts.fallbackPort + return port != null ? `${osIp}:${port}` : null + }, + ) + } + return { + const: async () => (await watchable()).const(), + once: async () => (await watchable()).once(), + } +} + export const smpPort = 5223 export const smpControlPort = 5224 export const xftpPort = 5225 diff --git a/startos/versions/current.ts b/startos/versions/current.ts index 4b9a933..dd3a80d 100644 --- a/startos/versions/current.ts +++ b/startos/versions/current.ts @@ -10,11 +10,16 @@ import { smpServerIni } from '../fileModels/smpServer.ini' export const current = VersionInfo.of({ version: '6.5.2:1', releaseNotes: { - en_US: 'Internal updates (start-sdk 2.0.x)', - es_ES: 'Actualizaciones internas (start-sdk 2.0.x)', - de_DE: 'Interne Aktualisierungen (start-sdk 2.0.x)', - pl_PL: 'Aktualizacje wewnętrzne (start-sdk 2.0.x)', - fr_FR: 'Mises à jour internes (start-sdk 2.0.x)', + en_US: + 'Internal updates (start-sdk 2.0.x). The server now reaches Tor over the internal network bridge and no longer restarts when Tor is updated; installing or removing Tor reconfigures the proxy automatically with a single restart.', + es_ES: + 'Actualizaciones internas (start-sdk 2.0.x). El servidor ahora alcanza Tor a través del puente de red interno y ya no se reinicia cuando Tor se actualiza; instalar o eliminar Tor reconfigura el proxy automáticamente con un solo reinicio.', + de_DE: + 'Interne Aktualisierungen (start-sdk 2.0.x). Der Server erreicht Tor jetzt über die interne Netzwerk-Bridge und startet nicht mehr neu, wenn Tor aktualisiert wird; das Installieren oder Entfernen von Tor konfiguriert den Proxy automatisch mit einem einzigen Neustart um.', + pl_PL: + 'Aktualizacje wewnętrzne (start-sdk 2.0.x). Serwer łączy się teraz z Torem przez wewnętrzny mostek sieciowy i nie restartuje się już przy aktualizacji Tora; instalacja lub usunięcie Tora automatycznie rekonfiguruje proxy z jednym restartem.', + fr_FR: + "Mises à jour internes (start-sdk 2.0.x). Le serveur atteint désormais Tor via le pont réseau interne et ne redémarre plus lorsque Tor est mis à jour ; l'installation ou la suppression de Tor reconfigure le proxy automatiquement avec un seul redémarrage.", }, migrations: { up: async ({ effects }) => { From 5afe010e2a4e654b8131b8d3d8dbe1285a40dccc Mon Sep 17 00:00:00 2001 From: Matt Hill <9935159+MattDHill@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:12:08 -0600 Subject: [PATCH 3/3] fix: apply PR review feedback - no fabricated dependency addresses: absent dependency => absent config/env value (optional fields, omitted keys), never a fake 127.0.0.1 dial default - dependency-task accept entries match absent-when-default fields (prune: 0 or absent both accepted) - neutral naming: the Bitcoin dependency is 'Bitcoin', not 'Bitcoin Core'/'Bitcoin Knots', across prose, release notes, and i18n - instructions.md keeps no version/migration notes - remove orphaned i18n entries added by the migration - normalize the bridgeAddress helper to an explicit early-return and prettier-format wave-touched files Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01PpJxhk25X42WTafMdkUx6t --- startos/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/startos/utils.ts b/startos/utils.ts index d9f0daa..2c5e0df 100644 --- a/startos/utils.ts +++ b/startos/utils.ts @@ -42,7 +42,8 @@ export function bridgeAddress( const port = host?.bindings[opts.internalPort]?.net.assignedPort ?? opts.fallbackPort - return port != null ? `${osIp}:${port}` : null + if (port == null) return null + return `${osIp}:${port}` }, ) }