@hasna/machines 0.0.24 → 0.0.25
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 +26 -0
- package/dist/agent/index.js +1 -1
- package/dist/cli/index.js +157 -30
- package/dist/commands/apps.d.ts.map +1 -1
- package/dist/commands/manifest.d.ts.map +1 -1
- package/dist/commands/screen.d.ts +35 -0
- package/dist/commands/screen.d.ts.map +1 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/consumer.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +76 -19
- package/dist/manifests.d.ts.map +1 -1
- package/dist/mcp/index.js +31 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,6 +118,32 @@ machines topology --no-tailscale --json
|
|
|
118
118
|
machines route --machine spark01 --json
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
+
## Screen sharing
|
|
122
|
+
|
|
123
|
+
Open Screen Sharing (VNC) to any machine using its best live route — no stale
|
|
124
|
+
IP bookmarks. The route resolver picks the current LAN address or Tailscale name
|
|
125
|
+
automatically, so it keeps working even when DHCP rotates a machine's IP.
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
machines screen machine005 # open Screen Sharing.app → vnc://<user>@<live-route>
|
|
129
|
+
machines screen machine005 --print # print the vnc:// URL instead of opening
|
|
130
|
+
machines screen machine005 --json # full resolution detail (route, confidence, user)
|
|
131
|
+
machines screen --all # open every reachable machine
|
|
132
|
+
machines screen --all --print # list resolved vnc:// URLs for the whole fleet
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Enable Remote Management / Screen Sharing on a fresh macOS machine over SSH
|
|
136
|
+
(kickstart + SRP + legacy VNC password so user-password auth works from
|
|
137
|
+
Screen Sharing.app and Apple Remote Desktop):
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
machines screen-enable --machine machine005 --user jo --vnc-password steaua17
|
|
141
|
+
machines screen-enable --machine machine005 --user jo --print # show the SSH command, don't run it
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`--vnc-password` is truncated to 8 characters by the legacy VNC protocol. The
|
|
145
|
+
user comes from the manifest (`metadata.user`) when present, or `--user`.
|
|
146
|
+
|
|
121
147
|
Consumers that need repo paths can resolve trust-aware workspace mappings
|
|
122
148
|
without importing the full machines app:
|
|
123
149
|
|
package/dist/agent/index.js
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -993,7 +993,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
993
993
|
this._exitCallback = (err) => {
|
|
994
994
|
if (err.code !== "commander.executeSubCommandAsync") {
|
|
995
995
|
throw err;
|
|
996
|
-
}
|
|
996
|
+
}
|
|
997
997
|
};
|
|
998
998
|
}
|
|
999
999
|
return this;
|
|
@@ -2601,6 +2601,9 @@ var {
|
|
|
2601
2601
|
Help
|
|
2602
2602
|
} = import__.default;
|
|
2603
2603
|
|
|
2604
|
+
// src/cli/index.ts
|
|
2605
|
+
import { execFileSync } from "child_process";
|
|
2606
|
+
|
|
2604
2607
|
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
2605
2608
|
var ANSI_BACKGROUND_OFFSET = 10;
|
|
2606
2609
|
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
@@ -7165,6 +7168,7 @@ function writeManifest(manifest, path = getManifestPath()) {
|
|
|
7165
7168
|
generatedAt: new Date().toISOString(),
|
|
7166
7169
|
machines: normalizeMachines(manifest.machines)
|
|
7167
7170
|
};
|
|
7171
|
+
fleetSchema.parse(payload);
|
|
7168
7172
|
writeFileSync(path, `${JSON.stringify(payload, null, 2)}
|
|
7169
7173
|
`, "utf8");
|
|
7170
7174
|
return path;
|
|
@@ -7200,9 +7204,10 @@ function manifestList() {
|
|
|
7200
7204
|
return readManifest();
|
|
7201
7205
|
}
|
|
7202
7206
|
function manifestAdd(machine) {
|
|
7207
|
+
const validatedMachine = machineSchema.parse(machine);
|
|
7203
7208
|
const manifest = readManifest();
|
|
7204
|
-
const nextMachines = manifest.machines.filter((entry) => entry.id !==
|
|
7205
|
-
nextMachines.push(
|
|
7209
|
+
const nextMachines = manifest.machines.filter((entry) => entry.id !== validatedMachine.id);
|
|
7210
|
+
nextMachines.push(validatedMachine);
|
|
7206
7211
|
const nextManifest = { ...manifest, machines: nextMachines };
|
|
7207
7212
|
writeManifest(nextManifest);
|
|
7208
7213
|
return nextManifest;
|
|
@@ -7227,6 +7232,7 @@ function manifestValidate() {
|
|
|
7227
7232
|
}
|
|
7228
7233
|
|
|
7229
7234
|
// src/commands/setup.ts
|
|
7235
|
+
import { homedir as homedir2 } from "os";
|
|
7230
7236
|
init_db();
|
|
7231
7237
|
function quote(value) {
|
|
7232
7238
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -7275,11 +7281,11 @@ function buildPackageSteps(machine) {
|
|
|
7275
7281
|
const manager = pkg.manager || (machine.platform === "macos" ? "brew" : "apt");
|
|
7276
7282
|
let command = pkg.name;
|
|
7277
7283
|
if (manager === "bun") {
|
|
7278
|
-
command = `bun install -g ${pkg.name}`;
|
|
7284
|
+
command = `bun install -g ${quote(pkg.name)}`;
|
|
7279
7285
|
} else if (manager === "brew") {
|
|
7280
|
-
command = `brew install ${pkg.name}`;
|
|
7286
|
+
command = `brew install ${quote(pkg.name)}`;
|
|
7281
7287
|
} else if (manager === "apt") {
|
|
7282
|
-
command = `sudo apt-get install -y ${pkg.name}`;
|
|
7288
|
+
command = `sudo apt-get install -y ${quote(pkg.name)}`;
|
|
7283
7289
|
}
|
|
7284
7290
|
return {
|
|
7285
7291
|
id: `package-${index + 1}`,
|
|
@@ -7297,7 +7303,7 @@ function buildSetupPlan(machineId) {
|
|
|
7297
7303
|
const target = selected || {
|
|
7298
7304
|
id: currentMachineId,
|
|
7299
7305
|
platform: "linux",
|
|
7300
|
-
workspacePath:
|
|
7306
|
+
workspacePath: `${homedir2()}/workspace`
|
|
7301
7307
|
};
|
|
7302
7308
|
const steps = [...buildBaseSteps(target), ...buildPackageSteps(target)];
|
|
7303
7309
|
return {
|
|
@@ -7343,7 +7349,7 @@ function runSetup(machineId, options = {}) {
|
|
|
7343
7349
|
}
|
|
7344
7350
|
|
|
7345
7351
|
// src/commands/backup.ts
|
|
7346
|
-
import { homedir as
|
|
7352
|
+
import { homedir as homedir3, hostname as hostname3 } from "os";
|
|
7347
7353
|
import { join as join3 } from "path";
|
|
7348
7354
|
var MACHINES_BACKUP_BUCKET_ENV = "HASNA_MACHINES_S3_BUCKET";
|
|
7349
7355
|
var MACHINES_BACKUP_BUCKET_FALLBACK_ENV = "MACHINES_S3_BUCKET";
|
|
@@ -7392,7 +7398,7 @@ function resolveBackupTarget(options = {}) {
|
|
|
7392
7398
|
};
|
|
7393
7399
|
}
|
|
7394
7400
|
function defaultBackupSources() {
|
|
7395
|
-
const home =
|
|
7401
|
+
const home = homedir3();
|
|
7396
7402
|
return [
|
|
7397
7403
|
join3(home, ".hasna"),
|
|
7398
7404
|
join3(home, ".ssh"),
|
|
@@ -7401,7 +7407,7 @@ function defaultBackupSources() {
|
|
|
7401
7407
|
}
|
|
7402
7408
|
function buildBackupPlan(bucket, prefix) {
|
|
7403
7409
|
const target = resolveBackupTarget({ bucket, prefix });
|
|
7404
|
-
const archivePath = join3(
|
|
7410
|
+
const archivePath = join3(homedir3(), ".hasna", "machines", "backup.tgz");
|
|
7405
7411
|
const sources = defaultBackupSources();
|
|
7406
7412
|
const steps = [
|
|
7407
7413
|
{
|
|
@@ -7413,7 +7419,7 @@ function buildBackupPlan(bucket, prefix) {
|
|
|
7413
7419
|
{
|
|
7414
7420
|
id: "backup-upload",
|
|
7415
7421
|
title: "Upload archive to S3",
|
|
7416
|
-
command: `aws s3 cp ${quote2(archivePath)} s3://${target.bucket}/${target.prefix}/$(
|
|
7422
|
+
command: `aws s3 cp ${quote2(archivePath)} ${quote2(`s3://${target.bucket}/${target.prefix}/${hostname3()}-backup.tgz`)}`,
|
|
7417
7423
|
manager: "custom"
|
|
7418
7424
|
}
|
|
7419
7425
|
];
|
|
@@ -7452,13 +7458,13 @@ function runBackup(bucket, prefix, options = {}) {
|
|
|
7452
7458
|
}
|
|
7453
7459
|
|
|
7454
7460
|
// src/commands/cert.ts
|
|
7455
|
-
import { homedir as
|
|
7461
|
+
import { homedir as homedir4, platform as platform2 } from "os";
|
|
7456
7462
|
import { join as join4 } from "path";
|
|
7457
7463
|
function quote3(value) {
|
|
7458
7464
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
7459
7465
|
}
|
|
7460
7466
|
function certDir() {
|
|
7461
|
-
return join4(
|
|
7467
|
+
return join4(homedir4(), ".hasna", "machines", "certs");
|
|
7462
7468
|
}
|
|
7463
7469
|
function buildCertPlan(domains) {
|
|
7464
7470
|
if (domains.length === 0) {
|
|
@@ -7618,12 +7624,12 @@ function diffMachines(leftMachineId, rightMachineId) {
|
|
|
7618
7624
|
// src/remote.ts
|
|
7619
7625
|
init_db();
|
|
7620
7626
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
7621
|
-
import { hostname as
|
|
7627
|
+
import { hostname as hostname5 } from "os";
|
|
7622
7628
|
|
|
7623
7629
|
// src/topology.ts
|
|
7624
7630
|
init_db();
|
|
7625
7631
|
import { existsSync as existsSync5 } from "fs";
|
|
7626
|
-
import { arch as arch2, hostname as
|
|
7632
|
+
import { arch as arch2, hostname as hostname4, platform as platform3, userInfo as userInfo2 } from "os";
|
|
7627
7633
|
import { spawnSync } from "child_process";
|
|
7628
7634
|
init_paths();
|
|
7629
7635
|
var MACHINES_CONSUMER_CONTRACT_VERSION = 1;
|
|
@@ -7847,7 +7853,7 @@ function discoverMachineTopology(options = {}) {
|
|
|
7847
7853
|
capabilities: getMachinesConsumerCapabilities(),
|
|
7848
7854
|
generated_at: now.toISOString(),
|
|
7849
7855
|
local_machine_id: localMachineId,
|
|
7850
|
-
local_hostname:
|
|
7856
|
+
local_hostname: hostname4(),
|
|
7851
7857
|
current_platform: normalizePlatform2(),
|
|
7852
7858
|
manifest_path_known: existsSync5(getManifestPath()),
|
|
7853
7859
|
machines,
|
|
@@ -7872,7 +7878,7 @@ function routeTargetMatches(machine, requested) {
|
|
|
7872
7878
|
}
|
|
7873
7879
|
function findRouteMachine(topology, requestedMachineId) {
|
|
7874
7880
|
const requested = normalizeMachineAlias(requestedMachineId);
|
|
7875
|
-
if (requested === "local" || requested === "localhost" || requested === normalizeMachineAlias(
|
|
7881
|
+
if (requested === "local" || requested === "localhost" || requested === normalizeMachineAlias(hostname4()) || requested === normalizeMachineAlias(topology.local_machine_id)) {
|
|
7876
7882
|
return {
|
|
7877
7883
|
machine: topology.machines.find((machine) => machine.machine_id === topology.local_machine_id) ?? null,
|
|
7878
7884
|
matchedBy: "local_alias"
|
|
@@ -8497,7 +8503,7 @@ function shellQuote3(value) {
|
|
|
8497
8503
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
8498
8504
|
}
|
|
8499
8505
|
function machineIsLocal(machineId, localMachineId) {
|
|
8500
|
-
return machineId === "local" || machineId === "localhost" || machineId === localMachineId || machineId ===
|
|
8506
|
+
return machineId === "local" || machineId === "localhost" || machineId === localMachineId || machineId === hostname5();
|
|
8501
8507
|
}
|
|
8502
8508
|
function resolveMachineCommand(machineId, command, localMachineId = getLocalMachineId()) {
|
|
8503
8509
|
if (machineIsLocal(machineId, localMachineId)) {
|
|
@@ -8549,20 +8555,21 @@ function shellQuote4(value) {
|
|
|
8549
8555
|
}
|
|
8550
8556
|
function buildAppCommand(machine, app) {
|
|
8551
8557
|
const packageName = getPackageName(app);
|
|
8558
|
+
const quotedPackageName = shellQuote4(packageName);
|
|
8552
8559
|
const manager = getAppManager(machine, app);
|
|
8553
8560
|
if (manager === "custom") {
|
|
8554
8561
|
return packageName;
|
|
8555
8562
|
}
|
|
8556
8563
|
if (machine.platform === "macos") {
|
|
8557
8564
|
if (manager === "cask") {
|
|
8558
|
-
return `brew install --cask ${
|
|
8565
|
+
return `brew install --cask ${quotedPackageName}`;
|
|
8559
8566
|
}
|
|
8560
|
-
return `brew install ${
|
|
8567
|
+
return `brew install ${quotedPackageName}`;
|
|
8561
8568
|
}
|
|
8562
8569
|
if (machine.platform === "windows") {
|
|
8563
|
-
return `winget install ${
|
|
8570
|
+
return `winget install ${quotedPackageName}`;
|
|
8564
8571
|
}
|
|
8565
|
-
return `sudo apt-get install -y ${
|
|
8572
|
+
return `sudo apt-get install -y ${quotedPackageName}`;
|
|
8566
8573
|
}
|
|
8567
8574
|
function buildAppProbeCommand(machine, app) {
|
|
8568
8575
|
const packageName = shellQuote4(getPackageName(app));
|
|
@@ -9145,8 +9152,55 @@ function listPorts(machineId) {
|
|
|
9145
9152
|
};
|
|
9146
9153
|
}
|
|
9147
9154
|
|
|
9155
|
+
// src/commands/screen.ts
|
|
9156
|
+
function shellQuote6(value) {
|
|
9157
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
9158
|
+
}
|
|
9159
|
+
function splitTarget(target) {
|
|
9160
|
+
const at = target.indexOf("@");
|
|
9161
|
+
if (at === -1)
|
|
9162
|
+
return [null, target];
|
|
9163
|
+
return [target.slice(0, at), target.slice(at + 1)];
|
|
9164
|
+
}
|
|
9165
|
+
function resolveScreenTarget(machineId, options = {}) {
|
|
9166
|
+
const resolved = resolveMachineRoute(machineId, options);
|
|
9167
|
+
if (!resolved.ok || !resolved.target) {
|
|
9168
|
+
throw new Error(`Machine route not found: ${machineId}`);
|
|
9169
|
+
}
|
|
9170
|
+
if (resolved.route === "unknown") {
|
|
9171
|
+
throw new Error(`Machine route is not reachable for screen sharing: ${machineId}`);
|
|
9172
|
+
}
|
|
9173
|
+
let [user, host] = splitTarget(resolved.target);
|
|
9174
|
+
if (!user) {
|
|
9175
|
+
const topology = options.topology ?? discoverMachineTopology(options);
|
|
9176
|
+
const entry = topology.machines.find((m) => m.machine_id === (resolved.machine_id ?? machineId));
|
|
9177
|
+
user = entry?.user ?? null;
|
|
9178
|
+
}
|
|
9179
|
+
const url = user ? `vnc://${user}@${host}` : `vnc://${host}`;
|
|
9180
|
+
return {
|
|
9181
|
+
machineId: resolved.machine_id ?? machineId,
|
|
9182
|
+
user,
|
|
9183
|
+
host,
|
|
9184
|
+
url,
|
|
9185
|
+
route: resolved.route,
|
|
9186
|
+
confidence: resolved.confidence,
|
|
9187
|
+
warnings: resolved.warnings
|
|
9188
|
+
};
|
|
9189
|
+
}
|
|
9190
|
+
function buildScreenEnableRemoteCommand(user, vncPassword) {
|
|
9191
|
+
const kickstart = "/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart";
|
|
9192
|
+
const lines = [
|
|
9193
|
+
`dseditgroup -o edit -a ${shellQuote6(user)} -t user com.apple.access_screensharing 2>/dev/null || true`,
|
|
9194
|
+
"defaults write /Library/Preferences/com.apple.RemoteManagement AllowSRPForNetworkNodes -bool true",
|
|
9195
|
+
`${kickstart} -configure -clientopts -setvnclegacy -vnclegacy yes -setvncpw -vncpw ${shellQuote6(vncPassword)}`,
|
|
9196
|
+
`${kickstart} -activate -configure -access -on -users ${shellQuote6(user)} -privs -all -restart -agent -menu`
|
|
9197
|
+
];
|
|
9198
|
+
return lines.join(" && ");
|
|
9199
|
+
}
|
|
9200
|
+
|
|
9148
9201
|
// src/commands/sync.ts
|
|
9149
9202
|
import { existsSync as existsSync7, lstatSync, readFileSync as readFileSync5, symlinkSync, copyFileSync } from "fs";
|
|
9203
|
+
import { homedir as homedir5 } from "os";
|
|
9150
9204
|
init_paths();
|
|
9151
9205
|
init_db();
|
|
9152
9206
|
function quote4(value) {
|
|
@@ -9166,13 +9220,13 @@ function packageCheckCommand(machine, packageName, manager = machine.platform ==
|
|
|
9166
9220
|
}
|
|
9167
9221
|
function packageInstallCommand(machine, packageName, manager = machine.platform === "macos" ? "brew" : "apt") {
|
|
9168
9222
|
if (manager === "bun") {
|
|
9169
|
-
return `bun install -g ${packageName}`;
|
|
9223
|
+
return `bun install -g ${quote4(packageName)}`;
|
|
9170
9224
|
}
|
|
9171
9225
|
if (manager === "brew") {
|
|
9172
|
-
return `brew install ${packageName}`;
|
|
9226
|
+
return `brew install ${quote4(packageName)}`;
|
|
9173
9227
|
}
|
|
9174
9228
|
if (manager === "apt") {
|
|
9175
|
-
return `sudo apt-get install -y ${packageName}`;
|
|
9229
|
+
return `sudo apt-get install -y ${quote4(packageName)}`;
|
|
9176
9230
|
}
|
|
9177
9231
|
return packageName;
|
|
9178
9232
|
}
|
|
@@ -9225,7 +9279,7 @@ function buildSyncPlan(machineId) {
|
|
|
9225
9279
|
const target = selected || {
|
|
9226
9280
|
id: currentMachineId,
|
|
9227
9281
|
platform: "linux",
|
|
9228
|
-
workspacePath:
|
|
9282
|
+
workspacePath: `${homedir5()}/workspace`
|
|
9229
9283
|
};
|
|
9230
9284
|
const actions = [
|
|
9231
9285
|
...detectPackageActions(target),
|
|
@@ -9492,7 +9546,7 @@ var DEFAULT_COMMANDS = [
|
|
|
9492
9546
|
function defaultPackages() {
|
|
9493
9547
|
return [{ name: "@hasna/machines", command: "machines", expectedVersion: getPackageVersion(), required: true }];
|
|
9494
9548
|
}
|
|
9495
|
-
function
|
|
9549
|
+
function shellQuote7(value) {
|
|
9496
9550
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
9497
9551
|
}
|
|
9498
9552
|
function commandId(value) {
|
|
@@ -9543,7 +9597,7 @@ function defaultRunner2(machineId, command) {
|
|
|
9543
9597
|
return runMachineCommand(machineId, command);
|
|
9544
9598
|
}
|
|
9545
9599
|
function inspectCommand(machineId, spec, runner) {
|
|
9546
|
-
const command =
|
|
9600
|
+
const command = shellQuote7(spec.command);
|
|
9547
9601
|
const versionArgs = spec.versionArgs ?? "--version";
|
|
9548
9602
|
const script = [
|
|
9549
9603
|
`cmd=${command}`,
|
|
@@ -9572,7 +9626,7 @@ function fieldCommand(field) {
|
|
|
9572
9626
|
}
|
|
9573
9627
|
function inspectWorkspace(machineId, spec, runner) {
|
|
9574
9628
|
const script = [
|
|
9575
|
-
`path=${
|
|
9629
|
+
`path=${shellQuote7(spec.path)}`,
|
|
9576
9630
|
'printf "exists=%s\\n" "$(test -d "$path" && printf yes || printf no)"',
|
|
9577
9631
|
'pkg="$path/package.json"',
|
|
9578
9632
|
'printf "package_json=%s\\n" "$(test -f "$pkg" && printf yes || printf no)"',
|
|
@@ -11318,6 +11372,11 @@ manifestCommand.command("add").description("Add or replace a machine in the flee
|
|
|
11318
11372
|
console.log(JSON.stringify(manifestAdd(machine2), null, 2));
|
|
11319
11373
|
return;
|
|
11320
11374
|
}
|
|
11375
|
+
for (const key of ["id", "platform", "workspacePath"]) {
|
|
11376
|
+
if (typeof options[key] !== "string" || !options[key].trim()) {
|
|
11377
|
+
throw new Error(`manifest add requires --${key === "workspacePath" ? "workspace-path" : key} unless --from-stdin is used`);
|
|
11378
|
+
}
|
|
11379
|
+
}
|
|
11321
11380
|
const packages = Array.isArray(options["package"]) ? options["package"].map((name) => ({ name: String(name) })) : undefined;
|
|
11322
11381
|
const files = Array.isArray(options["file"]) ? options["file"].map((value) => {
|
|
11323
11382
|
const [source, target, mode] = String(value).split(":");
|
|
@@ -11623,6 +11682,68 @@ program2.command("ssh").description("Choose the best SSH route for a machine").r
|
|
|
11623
11682
|
}
|
|
11624
11683
|
console.log(buildSshCommand(options.machine, options.cmd));
|
|
11625
11684
|
});
|
|
11685
|
+
program2.command("screen").description("Open Screen Sharing (VNC) to a machine using its best live route").argument("[machine]", "Machine identifier").option("--machine <id>", "Machine identifier (alternative to positional arg)").option("--all", "Open every reachable machine", false).option("--print", "Print the vnc:// URL instead of opening it", false).option("-j, --json", "Print JSON output", false).action((machineArg, options) => {
|
|
11686
|
+
if (options.all) {
|
|
11687
|
+
const topology = discoverMachineTopology();
|
|
11688
|
+
const results = topology.machines.map((m) => {
|
|
11689
|
+
try {
|
|
11690
|
+
const resolved2 = resolveScreenTarget(m.machine_id, { topology });
|
|
11691
|
+
return { machine: m.machine_id, ok: true, url: resolved2.url, route: resolved2.route };
|
|
11692
|
+
} catch (error) {
|
|
11693
|
+
return { machine: m.machine_id, ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
11694
|
+
}
|
|
11695
|
+
});
|
|
11696
|
+
if (options.json) {
|
|
11697
|
+
console.log(JSON.stringify(results, null, 2));
|
|
11698
|
+
return;
|
|
11699
|
+
}
|
|
11700
|
+
for (const r of results) {
|
|
11701
|
+
if (r.ok && r.url) {
|
|
11702
|
+
if (!options.print)
|
|
11703
|
+
execFileSync("open", [r.url], { stdio: "ignore" });
|
|
11704
|
+
console.log(`${r.ok ? "\u2713" : "\u2717"} ${r.machine.padEnd(14)} ${r.url ?? r.error}`);
|
|
11705
|
+
} else {
|
|
11706
|
+
console.log(`\u2717 ${r.machine.padEnd(14)} ${r.error}`);
|
|
11707
|
+
}
|
|
11708
|
+
}
|
|
11709
|
+
return;
|
|
11710
|
+
}
|
|
11711
|
+
const machineId = machineArg ?? options.machine;
|
|
11712
|
+
if (!machineId) {
|
|
11713
|
+
console.error("Provide a machine: machines screen <id> (or --all)");
|
|
11714
|
+
process.exitCode = 1;
|
|
11715
|
+
return;
|
|
11716
|
+
}
|
|
11717
|
+
const resolved = resolveScreenTarget(machineId);
|
|
11718
|
+
if (options.json) {
|
|
11719
|
+
console.log(JSON.stringify(resolved, null, 2));
|
|
11720
|
+
return;
|
|
11721
|
+
}
|
|
11722
|
+
if (options.print) {
|
|
11723
|
+
console.log(resolved.url);
|
|
11724
|
+
return;
|
|
11725
|
+
}
|
|
11726
|
+
execFileSync("open", [resolved.url], { stdio: "ignore" });
|
|
11727
|
+
console.log(`Opening Screen Sharing \u2192 ${resolved.url} (route: ${resolved.route})`);
|
|
11728
|
+
});
|
|
11729
|
+
program2.command("screen-enable").description("Enable Remote Management / Screen Sharing on a macOS machine over SSH").requiredOption("--machine <id>", "Machine identifier").option("--user <user>", "macOS user to grant screen-sharing (overrides manifest)").option("--vnc-password <pw>", "Legacy VNC password (<=8 chars honored)", "").option("--print", "Print the remote command instead of running it", false).action((options) => {
|
|
11730
|
+
const screen = resolveScreenTarget(options.machine);
|
|
11731
|
+
const user = options.user ?? screen.user;
|
|
11732
|
+
if (!user) {
|
|
11733
|
+
console.error(`No SSH user known for ${options.machine}; pass --user <name> or set metadata.user in the manifest.`);
|
|
11734
|
+
process.exitCode = 1;
|
|
11735
|
+
return;
|
|
11736
|
+
}
|
|
11737
|
+
const vncPw = (options.vncPassword || "").slice(0, 8);
|
|
11738
|
+
const remoteCmd = buildScreenEnableRemoteCommand(user, vncPw);
|
|
11739
|
+
const sshCmd = buildSshCommand(options.machine, `sudo -p '' bash -c ${JSON.stringify(remoteCmd)}`);
|
|
11740
|
+
if (options.print) {
|
|
11741
|
+
console.log(sshCmd);
|
|
11742
|
+
return;
|
|
11743
|
+
}
|
|
11744
|
+
console.log(`Run this to enable Screen Sharing on ${options.machine} (will prompt for sudo on the target):`);
|
|
11745
|
+
console.log(` ${sshCmd}`);
|
|
11746
|
+
});
|
|
11626
11747
|
program2.command("ports").description("List listening ports on a machine").option("--machine <id>", "Machine identifier").option("-j, --json", "Print JSON output", false).action((options) => {
|
|
11627
11748
|
const result = listPorts(options.machine);
|
|
11628
11749
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -11787,4 +11908,10 @@ healCommand.command("uninstall").description("Remove the systemd watchdog servic
|
|
|
11787
11908
|
}
|
|
11788
11909
|
console.log(renderList("uninstall", uninstallHealService()));
|
|
11789
11910
|
});
|
|
11790
|
-
|
|
11911
|
+
try {
|
|
11912
|
+
await program2.parseAsync(process.argv);
|
|
11913
|
+
} catch (error) {
|
|
11914
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11915
|
+
console.error(source_default.red(message));
|
|
11916
|
+
process.exit(1);
|
|
11917
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/commands/apps.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAGhB,eAAe,EACf,WAAW,EAEZ,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/commands/apps.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAGhB,eAAe,EACf,WAAW,EAEZ,MAAM,aAAa,CAAC;AA+FrB,wBAAgB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,eAAe,EAAE,CAAA;CAAE,CAM3F;AAED,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,CAQ7D;AAED,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAWlE;AAED,wBAAgB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,cAAc,CAO3D;AAED,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,WAAW,CA0BhH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAElE,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,YAAY,IAAI,aAAa,CAE5C;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,aAAa,CAQnE;AAED,wBAAgB,+BAA+B,IAAI,aAAa,CAE/D;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAErE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAQ/D;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type MachineRouteOptions, type MachineRouteKind, type MachineRouteConfidence } from "../topology.js";
|
|
2
|
+
export interface ResolvedScreenTarget {
|
|
3
|
+
machineId: string;
|
|
4
|
+
user: string | null;
|
|
5
|
+
host: string;
|
|
6
|
+
url: string;
|
|
7
|
+
route: MachineRouteKind;
|
|
8
|
+
confidence: MachineRouteConfidence;
|
|
9
|
+
warnings: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the best screen-sharing (VNC) target for a machine.
|
|
13
|
+
* Prefers the live LAN route over Tailscale (lower latency for screen sharing),
|
|
14
|
+
* and always produces a `vnc://user@host` URL when a user is known.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveScreenTarget(machineId: string, options?: MachineRouteOptions): ResolvedScreenTarget;
|
|
17
|
+
/**
|
|
18
|
+
* Build the macOS command that opens Screen Sharing to a machine.
|
|
19
|
+
* `open vnc://user@host` launches Screen Sharing.app pointed at the resolved route.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildScreenCommand(machineId: string, options?: MachineRouteOptions): string;
|
|
22
|
+
/**
|
|
23
|
+
* Build the remote command that ENABLES Remote Management / Screen Sharing on a
|
|
24
|
+
* macOS target via `kickstart`, plus the SRP + legacy-VNC password tweaks that
|
|
25
|
+
* make user-password auth work reliably from Screen Sharing.app and ARD.
|
|
26
|
+
*
|
|
27
|
+
* `vncPassword` is truncated to 8 chars by the VNC protocol; callers should pass
|
|
28
|
+
* an <=8 char value (or accept that only the first 8 chars are honored by legacy
|
|
29
|
+
* VNC clients).
|
|
30
|
+
*
|
|
31
|
+
* Returns the shell command to run AS ROOT on the target (caller pipes the sudo
|
|
32
|
+
* password or runs under an already-root context).
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildScreenEnableRemoteCommand(user: string, vncPassword: string): string;
|
|
35
|
+
//# sourceMappingURL=screen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../src/commands/screen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE5J,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,gBAAgB,CAAC;IACxB,UAAU,EAAE,sBAAsB,CAAC;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAgBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,oBAAoB,CA+B9G;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAG/F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAcxF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAmB,WAAW,EAAa,MAAM,aAAa,CAAC;AAsE3E,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,CAqB9D;AAED,wBAAgB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,WAAW,CAoC1G"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAA+B,UAAU,EAAE,MAAM,aAAa,CAAC;AAiF3E,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAwB5D;AAwBD,wBAAgB,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,UAAU,CAyCxG"}
|
package/dist/consumer.js
CHANGED
|
@@ -4264,6 +4264,7 @@ function writeManifest(manifest, path = getManifestPath()) {
|
|
|
4264
4264
|
generatedAt: new Date().toISOString(),
|
|
4265
4265
|
machines: normalizeMachines(manifest.machines)
|
|
4266
4266
|
};
|
|
4267
|
+
fleetSchema.parse(payload);
|
|
4267
4268
|
writeFileSync(path, `${JSON.stringify(payload, null, 2)}
|
|
4268
4269
|
`, "utf8");
|
|
4269
4270
|
return path;
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export * from "./commands/self-test.js";
|
|
|
21
21
|
export * from "./commands/serve.js";
|
|
22
22
|
export * from "./commands/setup.js";
|
|
23
23
|
export * from "./commands/ssh.js";
|
|
24
|
+
export * from "./commands/screen.js";
|
|
24
25
|
export * from "./commands/sync.js";
|
|
25
26
|
export * from "./commands/status.js";
|
|
26
27
|
export * from "./commands/workspace.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,kCAAkC,EAClC,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtH,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,kCAAkC,EAClC,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtH,cAAc,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11060,6 +11060,7 @@ function writeManifest(manifest, path = getManifestPath()) {
|
|
|
11060
11060
|
generatedAt: new Date().toISOString(),
|
|
11061
11061
|
machines: normalizeMachines(manifest.machines)
|
|
11062
11062
|
};
|
|
11063
|
+
fleetSchema.parse(payload);
|
|
11063
11064
|
writeFileSync(path, `${JSON.stringify(payload, null, 2)}
|
|
11064
11065
|
`, "utf8");
|
|
11065
11066
|
return path;
|
|
@@ -12465,7 +12466,7 @@ function getAgentStatus(machineId = getLocalMachineId()) {
|
|
|
12465
12466
|
}));
|
|
12466
12467
|
}
|
|
12467
12468
|
// src/commands/backup.ts
|
|
12468
|
-
import { homedir as homedir2 } from "os";
|
|
12469
|
+
import { homedir as homedir2, hostname as hostname5 } from "os";
|
|
12469
12470
|
import { join as join3 } from "path";
|
|
12470
12471
|
var MACHINES_BACKUP_BUCKET_ENV = "HASNA_MACHINES_S3_BUCKET";
|
|
12471
12472
|
var MACHINES_BACKUP_BUCKET_FALLBACK_ENV = "MACHINES_S3_BUCKET";
|
|
@@ -12535,7 +12536,7 @@ function buildBackupPlan(bucket, prefix) {
|
|
|
12535
12536
|
{
|
|
12536
12537
|
id: "backup-upload",
|
|
12537
12538
|
title: "Upload archive to S3",
|
|
12538
|
-
command: `aws s3 cp ${quote(archivePath)} s3://${target.bucket}/${target.prefix}/$(
|
|
12539
|
+
command: `aws s3 cp ${quote(archivePath)} ${quote(`s3://${target.bucket}/${target.prefix}/${hostname5()}-backup.tgz`)}`,
|
|
12539
12540
|
manager: "custom"
|
|
12540
12541
|
}
|
|
12541
12542
|
];
|
|
@@ -12590,20 +12591,21 @@ function shellQuote5(value) {
|
|
|
12590
12591
|
}
|
|
12591
12592
|
function buildAppCommand(machine, app) {
|
|
12592
12593
|
const packageName = getPackageName(app);
|
|
12594
|
+
const quotedPackageName = shellQuote5(packageName);
|
|
12593
12595
|
const manager = getAppManager(machine, app);
|
|
12594
12596
|
if (manager === "custom") {
|
|
12595
12597
|
return packageName;
|
|
12596
12598
|
}
|
|
12597
12599
|
if (machine.platform === "macos") {
|
|
12598
12600
|
if (manager === "cask") {
|
|
12599
|
-
return `brew install --cask ${
|
|
12601
|
+
return `brew install --cask ${quotedPackageName}`;
|
|
12600
12602
|
}
|
|
12601
|
-
return `brew install ${
|
|
12603
|
+
return `brew install ${quotedPackageName}`;
|
|
12602
12604
|
}
|
|
12603
12605
|
if (machine.platform === "windows") {
|
|
12604
|
-
return `winget install ${
|
|
12606
|
+
return `winget install ${quotedPackageName}`;
|
|
12605
12607
|
}
|
|
12606
|
-
return `sudo apt-get install -y ${
|
|
12608
|
+
return `sudo apt-get install -y ${quotedPackageName}`;
|
|
12607
12609
|
}
|
|
12608
12610
|
function buildAppProbeCommand(machine, app) {
|
|
12609
12611
|
const packageName = shellQuote5(getPackageName(app));
|
|
@@ -12889,9 +12891,10 @@ function manifestList() {
|
|
|
12889
12891
|
return readManifest();
|
|
12890
12892
|
}
|
|
12891
12893
|
function manifestAdd(machine) {
|
|
12894
|
+
const validatedMachine = machineSchema.parse(machine);
|
|
12892
12895
|
const manifest = readManifest();
|
|
12893
|
-
const nextMachines = manifest.machines.filter((entry) => entry.id !==
|
|
12894
|
-
nextMachines.push(
|
|
12896
|
+
const nextMachines = manifest.machines.filter((entry) => entry.id !== validatedMachine.id);
|
|
12897
|
+
nextMachines.push(validatedMachine);
|
|
12895
12898
|
const nextManifest = { ...manifest, machines: nextMachines };
|
|
12896
12899
|
writeManifest(nextManifest);
|
|
12897
12900
|
return nextManifest;
|
|
@@ -13741,6 +13744,7 @@ function runSelfTest() {
|
|
|
13741
13744
|
};
|
|
13742
13745
|
}
|
|
13743
13746
|
// src/commands/setup.ts
|
|
13747
|
+
import { homedir as homedir4 } from "os";
|
|
13744
13748
|
function quote3(value) {
|
|
13745
13749
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
13746
13750
|
}
|
|
@@ -13788,11 +13792,11 @@ function buildPackageSteps(machine) {
|
|
|
13788
13792
|
const manager = pkg.manager || (machine.platform === "macos" ? "brew" : "apt");
|
|
13789
13793
|
let command = pkg.name;
|
|
13790
13794
|
if (manager === "bun") {
|
|
13791
|
-
command = `bun install -g ${pkg.name}`;
|
|
13795
|
+
command = `bun install -g ${quote3(pkg.name)}`;
|
|
13792
13796
|
} else if (manager === "brew") {
|
|
13793
|
-
command = `brew install ${pkg.name}`;
|
|
13797
|
+
command = `brew install ${quote3(pkg.name)}`;
|
|
13794
13798
|
} else if (manager === "apt") {
|
|
13795
|
-
command = `sudo apt-get install -y ${pkg.name}`;
|
|
13799
|
+
command = `sudo apt-get install -y ${quote3(pkg.name)}`;
|
|
13796
13800
|
}
|
|
13797
13801
|
return {
|
|
13798
13802
|
id: `package-${index + 1}`,
|
|
@@ -13810,7 +13814,7 @@ function buildSetupPlan(machineId) {
|
|
|
13810
13814
|
const target = selected || {
|
|
13811
13815
|
id: currentMachineId,
|
|
13812
13816
|
platform: "linux",
|
|
13813
|
-
workspacePath:
|
|
13817
|
+
workspacePath: `${homedir4()}/workspace`
|
|
13814
13818
|
};
|
|
13815
13819
|
const steps = [...buildBaseSteps(target), ...buildPackageSteps(target)];
|
|
13816
13820
|
return {
|
|
@@ -13854,8 +13858,58 @@ function runSetup(machineId, options = {}) {
|
|
|
13854
13858
|
recordSetupRun(plan.machineId, "completed", summary);
|
|
13855
13859
|
return summary;
|
|
13856
13860
|
}
|
|
13861
|
+
// src/commands/screen.ts
|
|
13862
|
+
function shellQuote7(value) {
|
|
13863
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
13864
|
+
}
|
|
13865
|
+
function splitTarget(target) {
|
|
13866
|
+
const at = target.indexOf("@");
|
|
13867
|
+
if (at === -1)
|
|
13868
|
+
return [null, target];
|
|
13869
|
+
return [target.slice(0, at), target.slice(at + 1)];
|
|
13870
|
+
}
|
|
13871
|
+
function resolveScreenTarget(machineId, options = {}) {
|
|
13872
|
+
const resolved = resolveMachineRoute(machineId, options);
|
|
13873
|
+
if (!resolved.ok || !resolved.target) {
|
|
13874
|
+
throw new Error(`Machine route not found: ${machineId}`);
|
|
13875
|
+
}
|
|
13876
|
+
if (resolved.route === "unknown") {
|
|
13877
|
+
throw new Error(`Machine route is not reachable for screen sharing: ${machineId}`);
|
|
13878
|
+
}
|
|
13879
|
+
let [user, host] = splitTarget(resolved.target);
|
|
13880
|
+
if (!user) {
|
|
13881
|
+
const topology = options.topology ?? discoverMachineTopology(options);
|
|
13882
|
+
const entry = topology.machines.find((m) => m.machine_id === (resolved.machine_id ?? machineId));
|
|
13883
|
+
user = entry?.user ?? null;
|
|
13884
|
+
}
|
|
13885
|
+
const url = user ? `vnc://${user}@${host}` : `vnc://${host}`;
|
|
13886
|
+
return {
|
|
13887
|
+
machineId: resolved.machine_id ?? machineId,
|
|
13888
|
+
user,
|
|
13889
|
+
host,
|
|
13890
|
+
url,
|
|
13891
|
+
route: resolved.route,
|
|
13892
|
+
confidence: resolved.confidence,
|
|
13893
|
+
warnings: resolved.warnings
|
|
13894
|
+
};
|
|
13895
|
+
}
|
|
13896
|
+
function buildScreenCommand(machineId, options = {}) {
|
|
13897
|
+
const resolved = resolveScreenTarget(machineId, options);
|
|
13898
|
+
return `open ${resolved.url}`;
|
|
13899
|
+
}
|
|
13900
|
+
function buildScreenEnableRemoteCommand(user, vncPassword) {
|
|
13901
|
+
const kickstart = "/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart";
|
|
13902
|
+
const lines = [
|
|
13903
|
+
`dseditgroup -o edit -a ${shellQuote7(user)} -t user com.apple.access_screensharing 2>/dev/null || true`,
|
|
13904
|
+
"defaults write /Library/Preferences/com.apple.RemoteManagement AllowSRPForNetworkNodes -bool true",
|
|
13905
|
+
`${kickstart} -configure -clientopts -setvnclegacy -vnclegacy yes -setvncpw -vncpw ${shellQuote7(vncPassword)}`,
|
|
13906
|
+
`${kickstart} -activate -configure -access -on -users ${shellQuote7(user)} -privs -all -restart -agent -menu`
|
|
13907
|
+
];
|
|
13908
|
+
return lines.join(" && ");
|
|
13909
|
+
}
|
|
13857
13910
|
// src/commands/sync.ts
|
|
13858
13911
|
import { existsSync as existsSync7, lstatSync, readFileSync as readFileSync5, symlinkSync, copyFileSync } from "fs";
|
|
13912
|
+
import { homedir as homedir5 } from "os";
|
|
13859
13913
|
function quote4(value) {
|
|
13860
13914
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
13861
13915
|
}
|
|
@@ -13873,13 +13927,13 @@ function packageCheckCommand(machine, packageName, manager = machine.platform ==
|
|
|
13873
13927
|
}
|
|
13874
13928
|
function packageInstallCommand(machine, packageName, manager = machine.platform === "macos" ? "brew" : "apt") {
|
|
13875
13929
|
if (manager === "bun") {
|
|
13876
|
-
return `bun install -g ${packageName}`;
|
|
13930
|
+
return `bun install -g ${quote4(packageName)}`;
|
|
13877
13931
|
}
|
|
13878
13932
|
if (manager === "brew") {
|
|
13879
|
-
return `brew install ${packageName}`;
|
|
13933
|
+
return `brew install ${quote4(packageName)}`;
|
|
13880
13934
|
}
|
|
13881
13935
|
if (manager === "apt") {
|
|
13882
|
-
return `sudo apt-get install -y ${packageName}`;
|
|
13936
|
+
return `sudo apt-get install -y ${quote4(packageName)}`;
|
|
13883
13937
|
}
|
|
13884
13938
|
return packageName;
|
|
13885
13939
|
}
|
|
@@ -13932,7 +13986,7 @@ function buildSyncPlan(machineId) {
|
|
|
13932
13986
|
const target = selected || {
|
|
13933
13987
|
id: currentMachineId,
|
|
13934
13988
|
platform: "linux",
|
|
13935
|
-
workspacePath:
|
|
13989
|
+
workspacePath: `${homedir5()}/workspace`
|
|
13936
13990
|
};
|
|
13937
13991
|
const actions = [
|
|
13938
13992
|
...detectPackageActions(target),
|
|
@@ -14877,7 +14931,7 @@ var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]
|
|
|
14877
14931
|
var cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
|
|
14878
14932
|
var base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
|
|
14879
14933
|
var base64url = /^[A-Za-z0-9_-]*$/;
|
|
14880
|
-
var
|
|
14934
|
+
var hostname6 = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+$/;
|
|
14881
14935
|
var e164 = /^\+(?:[0-9]){6,14}[0-9]$/;
|
|
14882
14936
|
var dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;
|
|
14883
14937
|
var date = /* @__PURE__ */ new RegExp(`^${dateSource}$`);
|
|
@@ -15489,7 +15543,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
|
|
|
15489
15543
|
code: "invalid_format",
|
|
15490
15544
|
format: "url",
|
|
15491
15545
|
note: "Invalid hostname",
|
|
15492
|
-
pattern:
|
|
15546
|
+
pattern: hostname6.source,
|
|
15493
15547
|
input: payload.value,
|
|
15494
15548
|
inst,
|
|
15495
15549
|
continue: !def.abort
|
|
@@ -17478,7 +17532,7 @@ class JSONSchemaGenerator {
|
|
|
17478
17532
|
if (val === undefined) {
|
|
17479
17533
|
if (this.unrepresentable === "throw") {
|
|
17480
17534
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
17481
|
-
}
|
|
17535
|
+
}
|
|
17482
17536
|
} else if (typeof val === "bigint") {
|
|
17483
17537
|
if (this.unrepresentable === "throw") {
|
|
17484
17538
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -23307,6 +23361,7 @@ export {
|
|
|
23307
23361
|
runAppsInstall,
|
|
23308
23362
|
resolveTables,
|
|
23309
23363
|
resolveSshTarget,
|
|
23364
|
+
resolveScreenTarget,
|
|
23310
23365
|
resolveMachineWorkspace,
|
|
23311
23366
|
resolveMachineRoute,
|
|
23312
23367
|
resolveBackupTarget,
|
|
@@ -23380,6 +23435,8 @@ export {
|
|
|
23380
23435
|
buildSshCommand,
|
|
23381
23436
|
buildSetupPlan,
|
|
23382
23437
|
buildServer,
|
|
23438
|
+
buildScreenEnableRemoteCommand,
|
|
23439
|
+
buildScreenCommand,
|
|
23383
23440
|
buildClaudeInstallPlan,
|
|
23384
23441
|
buildCertPlan,
|
|
23385
23442
|
buildBackupPlan,
|
package/dist/manifests.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifests.d.ts","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAoBjE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAItB,CAAC;AAoBH,wBAAgB,kBAAkB,IAAI,aAAa,CAMlD;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAoB,GAAG,aAAa,CAMpE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,SAAoB,GAAG,aAAa,CAExE;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,SAAoB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"manifests.d.ts","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAoBjE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAItB,CAAC;AAoBH,wBAAgB,kBAAkB,IAAI,aAAa,CAMlD;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAoB,GAAG,aAAa,CAMpE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,SAAoB,GAAG,aAAa,CAExE;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,SAAoB,GAAG,MAAM,CAWvF;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,SAAoB,GAAG,eAAe,GAAG,IAAI,CAEtG;AAED,wBAAgB,4BAA4B,IAAI,eAAe,CAiB9D"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -4017,7 +4017,7 @@ var coerce = {
|
|
|
4017
4017
|
};
|
|
4018
4018
|
var NEVER = INVALID;
|
|
4019
4019
|
// src/commands/backup.ts
|
|
4020
|
-
import { homedir } from "os";
|
|
4020
|
+
import { homedir, hostname } from "os";
|
|
4021
4021
|
import { join as join2 } from "path";
|
|
4022
4022
|
var MACHINES_BACKUP_BUCKET_ENV = "HASNA_MACHINES_S3_BUCKET";
|
|
4023
4023
|
var MACHINES_BACKUP_BUCKET_FALLBACK_ENV = "MACHINES_S3_BUCKET";
|
|
@@ -4087,7 +4087,7 @@ function buildBackupPlan(bucket, prefix) {
|
|
|
4087
4087
|
{
|
|
4088
4088
|
id: "backup-upload",
|
|
4089
4089
|
title: "Upload archive to S3",
|
|
4090
|
-
command: `aws s3 cp ${quote(archivePath)} s3://${target.bucket}/${target.prefix}/$(
|
|
4090
|
+
command: `aws s3 cp ${quote(archivePath)} ${quote(`s3://${target.bucket}/${target.prefix}/${hostname()}-backup.tgz`)}`,
|
|
4091
4091
|
manager: "custom"
|
|
4092
4092
|
}
|
|
4093
4093
|
];
|
|
@@ -4127,7 +4127,7 @@ function runBackup(bucket, prefix, options = {}) {
|
|
|
4127
4127
|
|
|
4128
4128
|
// src/manifests.ts
|
|
4129
4129
|
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
4130
|
-
import { arch, homedir as homedir2, hostname, platform, userInfo } from "os";
|
|
4130
|
+
import { arch, homedir as homedir2, hostname as hostname2, platform, userInfo } from "os";
|
|
4131
4131
|
import { dirname as dirname3 } from "path";
|
|
4132
4132
|
|
|
4133
4133
|
// src/paths.ts
|
|
@@ -4235,6 +4235,7 @@ function writeManifest(manifest, path = getManifestPath()) {
|
|
|
4235
4235
|
generatedAt: new Date().toISOString(),
|
|
4236
4236
|
machines: normalizeMachines(manifest.machines)
|
|
4237
4237
|
};
|
|
4238
|
+
fleetSchema.parse(payload);
|
|
4238
4239
|
writeFileSync(path, `${JSON.stringify(payload, null, 2)}
|
|
4239
4240
|
`, "utf8");
|
|
4240
4241
|
return path;
|
|
@@ -4243,12 +4244,12 @@ function getManifestMachine(machineId, path = getManifestPath()) {
|
|
|
4243
4244
|
return readManifest(path).machines.find((machine) => machine.id === machineId) || null;
|
|
4244
4245
|
}
|
|
4245
4246
|
function detectCurrentMachineManifest() {
|
|
4246
|
-
const machineId = process.env["HASNA_MACHINES_MACHINE_ID"] ||
|
|
4247
|
+
const machineId = process.env["HASNA_MACHINES_MACHINE_ID"] || hostname2();
|
|
4247
4248
|
const user = userInfo().username;
|
|
4248
4249
|
const bunDir = dirname3(process.execPath);
|
|
4249
4250
|
return {
|
|
4250
4251
|
id: machineId,
|
|
4251
|
-
hostname:
|
|
4252
|
+
hostname: hostname2(),
|
|
4252
4253
|
sshAddress: `${user}@${machineId}`,
|
|
4253
4254
|
tailscaleName: machineId,
|
|
4254
4255
|
platform: normalizePlatform(),
|
|
@@ -4263,11 +4264,11 @@ function detectCurrentMachineManifest() {
|
|
|
4263
4264
|
|
|
4264
4265
|
// src/remote.ts
|
|
4265
4266
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4266
|
-
import { hostname as
|
|
4267
|
+
import { hostname as hostname5 } from "os";
|
|
4267
4268
|
|
|
4268
4269
|
// src/db.ts
|
|
4269
4270
|
import { Database } from "bun:sqlite";
|
|
4270
|
-
import { hostname as
|
|
4271
|
+
import { hostname as hostname3 } from "os";
|
|
4271
4272
|
class SqliteAdapter {
|
|
4272
4273
|
raw;
|
|
4273
4274
|
constructor(path) {
|
|
@@ -4332,7 +4333,7 @@ function getDb(path = getDbPath()) {
|
|
|
4332
4333
|
return getAdapter(path).raw;
|
|
4333
4334
|
}
|
|
4334
4335
|
function getLocalMachineId() {
|
|
4335
|
-
return process.env["HASNA_MACHINES_MACHINE_ID"] ||
|
|
4336
|
+
return process.env["HASNA_MACHINES_MACHINE_ID"] || hostname3();
|
|
4336
4337
|
}
|
|
4337
4338
|
function listHeartbeats(machineId) {
|
|
4338
4339
|
const db = getDb();
|
|
@@ -4366,7 +4367,7 @@ function recordSyncRun(machineId, status, actions) {
|
|
|
4366
4367
|
|
|
4367
4368
|
// src/topology.ts
|
|
4368
4369
|
import { existsSync as existsSync4 } from "fs";
|
|
4369
|
-
import { arch as arch2, hostname as
|
|
4370
|
+
import { arch as arch2, hostname as hostname4, platform as platform2, userInfo as userInfo2 } from "os";
|
|
4370
4371
|
import { spawnSync } from "child_process";
|
|
4371
4372
|
var MACHINES_CONSUMER_CONTRACT_VERSION = 1;
|
|
4372
4373
|
var MACHINES_PACKAGE_NAME = "@hasna/machines";
|
|
@@ -4589,7 +4590,7 @@ function discoverMachineTopology(options = {}) {
|
|
|
4589
4590
|
capabilities: getMachinesConsumerCapabilities(),
|
|
4590
4591
|
generated_at: now.toISOString(),
|
|
4591
4592
|
local_machine_id: localMachineId,
|
|
4592
|
-
local_hostname:
|
|
4593
|
+
local_hostname: hostname4(),
|
|
4593
4594
|
current_platform: normalizePlatform2(),
|
|
4594
4595
|
manifest_path_known: existsSync4(getManifestPath()),
|
|
4595
4596
|
machines,
|
|
@@ -4614,7 +4615,7 @@ function routeTargetMatches(machine, requested) {
|
|
|
4614
4615
|
}
|
|
4615
4616
|
function findRouteMachine(topology, requestedMachineId) {
|
|
4616
4617
|
const requested = normalizeMachineAlias(requestedMachineId);
|
|
4617
|
-
if (requested === "local" || requested === "localhost" || requested === normalizeMachineAlias(
|
|
4618
|
+
if (requested === "local" || requested === "localhost" || requested === normalizeMachineAlias(hostname4()) || requested === normalizeMachineAlias(topology.local_machine_id)) {
|
|
4618
4619
|
return {
|
|
4619
4620
|
machine: topology.machines.find((machine) => machine.machine_id === topology.local_machine_id) ?? null,
|
|
4620
4621
|
matchedBy: "local_alias"
|
|
@@ -5239,7 +5240,7 @@ function shellQuote3(value) {
|
|
|
5239
5240
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
5240
5241
|
}
|
|
5241
5242
|
function machineIsLocal(machineId, localMachineId) {
|
|
5242
|
-
return machineId === "local" || machineId === "localhost" || machineId === localMachineId || machineId ===
|
|
5243
|
+
return machineId === "local" || machineId === "localhost" || machineId === localMachineId || machineId === hostname5();
|
|
5243
5244
|
}
|
|
5244
5245
|
function resolveMachineCommand(machineId, command, localMachineId = getLocalMachineId()) {
|
|
5245
5246
|
if (machineIsLocal(machineId, localMachineId)) {
|
|
@@ -5291,20 +5292,21 @@ function shellQuote4(value) {
|
|
|
5291
5292
|
}
|
|
5292
5293
|
function buildAppCommand(machine, app) {
|
|
5293
5294
|
const packageName = getPackageName(app);
|
|
5295
|
+
const quotedPackageName = shellQuote4(packageName);
|
|
5294
5296
|
const manager = getAppManager(machine, app);
|
|
5295
5297
|
if (manager === "custom") {
|
|
5296
5298
|
return packageName;
|
|
5297
5299
|
}
|
|
5298
5300
|
if (machine.platform === "macos") {
|
|
5299
5301
|
if (manager === "cask") {
|
|
5300
|
-
return `brew install --cask ${
|
|
5302
|
+
return `brew install --cask ${quotedPackageName}`;
|
|
5301
5303
|
}
|
|
5302
|
-
return `brew install ${
|
|
5304
|
+
return `brew install ${quotedPackageName}`;
|
|
5303
5305
|
}
|
|
5304
5306
|
if (machine.platform === "windows") {
|
|
5305
|
-
return `winget install ${
|
|
5307
|
+
return `winget install ${quotedPackageName}`;
|
|
5306
5308
|
}
|
|
5307
|
-
return `sudo apt-get install -y ${
|
|
5309
|
+
return `sudo apt-get install -y ${quotedPackageName}`;
|
|
5308
5310
|
}
|
|
5309
5311
|
function buildAppProbeCommand(machine, app) {
|
|
5310
5312
|
const packageName = shellQuote4(getPackageName(app));
|
|
@@ -6106,9 +6108,10 @@ function manifestList() {
|
|
|
6106
6108
|
return readManifest();
|
|
6107
6109
|
}
|
|
6108
6110
|
function manifestAdd(machine) {
|
|
6111
|
+
const validatedMachine = machineSchema.parse(machine);
|
|
6109
6112
|
const manifest = readManifest();
|
|
6110
|
-
const nextMachines = manifest.machines.filter((entry) => entry.id !==
|
|
6111
|
-
nextMachines.push(
|
|
6113
|
+
const nextMachines = manifest.machines.filter((entry) => entry.id !== validatedMachine.id);
|
|
6114
|
+
nextMachines.push(validatedMachine);
|
|
6112
6115
|
const nextManifest = { ...manifest, machines: nextMachines };
|
|
6113
6116
|
writeManifest(nextManifest);
|
|
6114
6117
|
return nextManifest;
|
|
@@ -6374,6 +6377,7 @@ function renderDashboardHtml() {
|
|
|
6374
6377
|
}
|
|
6375
6378
|
|
|
6376
6379
|
// src/commands/setup.ts
|
|
6380
|
+
import { homedir as homedir4 } from "os";
|
|
6377
6381
|
function quote3(value) {
|
|
6378
6382
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
6379
6383
|
}
|
|
@@ -6421,11 +6425,11 @@ function buildPackageSteps(machine) {
|
|
|
6421
6425
|
const manager = pkg.manager || (machine.platform === "macos" ? "brew" : "apt");
|
|
6422
6426
|
let command = pkg.name;
|
|
6423
6427
|
if (manager === "bun") {
|
|
6424
|
-
command = `bun install -g ${pkg.name}`;
|
|
6428
|
+
command = `bun install -g ${quote3(pkg.name)}`;
|
|
6425
6429
|
} else if (manager === "brew") {
|
|
6426
|
-
command = `brew install ${pkg.name}`;
|
|
6430
|
+
command = `brew install ${quote3(pkg.name)}`;
|
|
6427
6431
|
} else if (manager === "apt") {
|
|
6428
|
-
command = `sudo apt-get install -y ${pkg.name}`;
|
|
6432
|
+
command = `sudo apt-get install -y ${quote3(pkg.name)}`;
|
|
6429
6433
|
}
|
|
6430
6434
|
return {
|
|
6431
6435
|
id: `package-${index + 1}`,
|
|
@@ -6443,7 +6447,7 @@ function buildSetupPlan(machineId) {
|
|
|
6443
6447
|
const target = selected || {
|
|
6444
6448
|
id: currentMachineId,
|
|
6445
6449
|
platform: "linux",
|
|
6446
|
-
workspacePath:
|
|
6450
|
+
workspacePath: `${homedir4()}/workspace`
|
|
6447
6451
|
};
|
|
6448
6452
|
const steps = [...buildBaseSteps(target), ...buildPackageSteps(target)];
|
|
6449
6453
|
return {
|
|
@@ -6490,6 +6494,7 @@ function runSetup(machineId, options = {}) {
|
|
|
6490
6494
|
|
|
6491
6495
|
// src/commands/sync.ts
|
|
6492
6496
|
import { existsSync as existsSync7, lstatSync, readFileSync as readFileSync5, symlinkSync, copyFileSync } from "fs";
|
|
6497
|
+
import { homedir as homedir5 } from "os";
|
|
6493
6498
|
function quote4(value) {
|
|
6494
6499
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
6495
6500
|
}
|
|
@@ -6507,13 +6512,13 @@ function packageCheckCommand(machine, packageName, manager = machine.platform ==
|
|
|
6507
6512
|
}
|
|
6508
6513
|
function packageInstallCommand(machine, packageName, manager = machine.platform === "macos" ? "brew" : "apt") {
|
|
6509
6514
|
if (manager === "bun") {
|
|
6510
|
-
return `bun install -g ${packageName}`;
|
|
6515
|
+
return `bun install -g ${quote4(packageName)}`;
|
|
6511
6516
|
}
|
|
6512
6517
|
if (manager === "brew") {
|
|
6513
|
-
return `brew install ${packageName}`;
|
|
6518
|
+
return `brew install ${quote4(packageName)}`;
|
|
6514
6519
|
}
|
|
6515
6520
|
if (manager === "apt") {
|
|
6516
|
-
return `sudo apt-get install -y ${packageName}`;
|
|
6521
|
+
return `sudo apt-get install -y ${quote4(packageName)}`;
|
|
6517
6522
|
}
|
|
6518
6523
|
return packageName;
|
|
6519
6524
|
}
|
|
@@ -6566,7 +6571,7 @@ function buildSyncPlan(machineId) {
|
|
|
6566
6571
|
const target = selected || {
|
|
6567
6572
|
id: currentMachineId,
|
|
6568
6573
|
platform: "linux",
|
|
6569
|
-
workspacePath:
|
|
6574
|
+
workspacePath: `${homedir5()}/workspace`
|
|
6570
6575
|
};
|
|
6571
6576
|
const actions = [
|
|
6572
6577
|
...detectPackageActions(target),
|