feat(onboarding): recommend plugins and skills from installed apps (#109668)

* feat(onboarding): recommend plugins and skills from installed apps

Scan installed macOS apps during classic onboarding (TCC-free), gather
candidates from official catalogs + ClawHub search, let the configured
model pick genuine matches, and offer an opt-in multiselect install step.
Adds a device.apps node-host command (default-off sharing, Android-parity
envelope) so remote gateways can request a paired Mac's inventory, and a
wizard.appRecommendations kill switch. Custom setup-inference completions
no longer inherit the 32-token verification-probe output cap.

* feat(onboarding): recommend apps in guided flow

* fix(onboarding): harden app recommendations against ClawHub self-promotion

Third-party ClawHub skills are never pre-selected regardless of model tier
(publisher-controlled listing text reaches the matcher prompt and could
promote itself); their labels now say they install third-party code.
Installed-app scans follow symlinked .app bundles. Matcher output stays
bounded by the resolved model's own maxTokens budget (documented invariant).

* fix(onboarding): key official catalog candidates by resolved plugin id

Real catalog entries are package manifests without a top-level id; keying the
candidate map and channel/provider classification by entry.id collapsed the
whole official catalog into one undefined-keyed entry, so no official plugin
or channel was ever recommended. Regression test runs against the bundled
catalogs.

* fix(onboarding): satisfy lint, types, deadcode, and migration gates

Split the guided-onboarding test into a self-contained custodian suite to stay
under max-lines. Narrow app-recommendation exports (drop dead node-payload
normalizer, unexport internal types/helpers, route candidate tests through the
public API), replace map-spread with a helper, unexport device.apps result
types, add installedAppsSharing to node-host migration expectations, cast the
wizard multiselect mock, and regenerate the docs map.

* test(onboarding): register new live test in the shard classifier
This commit is contained in:
Peter Steinberger
2026-07-17 14:07:59 +01:00
committed by GitHub
parent 8020dd3e08
commit da69daeb72
47 changed files with 2323 additions and 192 deletions

View File

@@ -46,6 +46,7 @@ type NodeDaemonInstallOptions = {
tlsFingerprint?: string;
nodeId?: string;
displayName?: string;
shareInstalledApps?: boolean;
runtime?: string;
force?: boolean;
json?: boolean;
@@ -162,6 +163,7 @@ export async function runNodeDaemonInstall(opts: NodeDaemonInstallOptions) {
tlsFingerprint: tlsFingerprint || undefined,
nodeId: opts.nodeId,
displayName: opts.displayName,
installedAppsSharing: opts.shareInstalledApps,
runtime: runtimeRaw,
warn: (message) => {
if (json) {

View File

@@ -65,6 +65,8 @@ export function registerNodeCli(program: Command) {
.option("--tls-fingerprint <sha256>", "Expected TLS certificate fingerprint (sha256)")
.option("--node-id <id>", "Override the generated node instance id")
.option("--display-name <name>", "Override node display name")
.option("--share-installed-apps", "Share installed macOS applications with the Gateway")
.option("--no-share-installed-apps", "Disable installed application sharing")
.action(async (opts) => {
const existing = await loadNodeHostConfig();
const host =
@@ -101,6 +103,7 @@ export function registerNodeCli(program: Command) {
(explicitContextPath || retargetedGateway ? undefined : existing?.gateway?.contextPath),
nodeId: opts.nodeId,
displayName: opts.displayName,
installedAppsSharing: opts.shareInstalledApps,
});
});
@@ -130,6 +133,8 @@ export function registerNodeCli(program: Command) {
.option("--tls-fingerprint <sha256>", "Expected TLS certificate fingerprint (sha256)")
.option("--node-id <id>", "Override the generated node instance id")
.option("--display-name <name>", "Override node display name")
.option("--share-installed-apps", "Share installed macOS applications with the Gateway")
.option("--no-share-installed-apps", "Disable installed application sharing")
.option("--runtime <runtime>", "Service runtime (node). Default: node")
.option("--force", "Reinstall/overwrite if already installed", false)
.option("--json", "Output JSON", false)