sim-setup 1.0.2-preview.48.1 → 1.0.3-preview.49.1
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 +16 -0
- package/dist/index.js +203 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -153,6 +153,22 @@ npx sim-setup doctor --json
|
|
|
153
153
|
Use `config` to answer “what is configured?” and `status` to answer “what is
|
|
154
154
|
running and healthy?”
|
|
155
155
|
|
|
156
|
+
### Install the desktop app
|
|
157
|
+
|
|
158
|
+
`desktop` resolves the macOS installer from your own deployment and prints the
|
|
159
|
+
server URL to enter in the app:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npx sim-setup desktop
|
|
163
|
+
npx sim-setup desktop --url https://sim.example.com
|
|
164
|
+
npx sim-setup desktop --no-open
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The desktop app is not tied to sim.ai — it bakes in only a default server, and
|
|
168
|
+
every Sim deployment already serves `/api/desktop/update/download` and the
|
|
169
|
+
update feed installed apps poll. Install the signed build, then point it at your
|
|
170
|
+
deployment with **Sim → Server…**. Nothing has to be built or signed by you.
|
|
171
|
+
|
|
156
172
|
### Add or change capabilities
|
|
157
173
|
|
|
158
174
|
Configure one capability without walking through the complete wizard:
|
package/dist/index.js
CHANGED
|
@@ -36106,6 +36106,178 @@ var init_feature_setup = __esm(() => {
|
|
|
36106
36106
|
init_theme();
|
|
36107
36107
|
});
|
|
36108
36108
|
|
|
36109
|
+
// src/urls.ts
|
|
36110
|
+
var APP_URL = "http://localhost:3000", APP_SIGNUP_URL;
|
|
36111
|
+
var init_urls = __esm(() => {
|
|
36112
|
+
APP_SIGNUP_URL = `${APP_URL}/signup`;
|
|
36113
|
+
});
|
|
36114
|
+
|
|
36115
|
+
// src/desktop.ts
|
|
36116
|
+
var exports_desktop = {};
|
|
36117
|
+
__export(exports_desktop, {
|
|
36118
|
+
sanitizeForTerminal: () => sanitizeForTerminal,
|
|
36119
|
+
runDesktop: () => runDesktop,
|
|
36120
|
+
resolveDeploymentUrl: () => resolveDeploymentUrl,
|
|
36121
|
+
probeDownload: () => probeDownload,
|
|
36122
|
+
describeProbe: () => describeProbe
|
|
36123
|
+
});
|
|
36124
|
+
function deploymentKey(value) {
|
|
36125
|
+
try {
|
|
36126
|
+
return new URL(value).origin.toLowerCase();
|
|
36127
|
+
} catch {
|
|
36128
|
+
return value;
|
|
36129
|
+
}
|
|
36130
|
+
}
|
|
36131
|
+
function sanitizeForTerminal(value) {
|
|
36132
|
+
const printable = value.replace(/\p{C}/gu, "").replace(/\p{Z}/gu, " ").replace(/ {2,}/g, " ").trim();
|
|
36133
|
+
return truncate(printable, MAX_INSTALLER_NAME);
|
|
36134
|
+
}
|
|
36135
|
+
function resolveDeploymentUrl(sources, override) {
|
|
36136
|
+
const discovered = sources.flatMap((source) => {
|
|
36137
|
+
const value = source.values?.get(APP_URL_KEY)?.trim();
|
|
36138
|
+
return value ? [{ label: source.label ?? "configuration", value }] : [];
|
|
36139
|
+
});
|
|
36140
|
+
const byDeployment = new Map;
|
|
36141
|
+
for (const { value } of discovered) {
|
|
36142
|
+
byDeployment.set(deploymentKey(value), value);
|
|
36143
|
+
}
|
|
36144
|
+
if (!override && byDeployment.size > 1) {
|
|
36145
|
+
throw new SetupError(`Found ${byDeployment.size} configurations naming different ${APP_URL_KEY} values.`, [
|
|
36146
|
+
...discovered.map(({ label, value }) => `${label}: ${value}`),
|
|
36147
|
+
"Re-run with --url <deployment url> to say which one the desktop app should use."
|
|
36148
|
+
]);
|
|
36149
|
+
}
|
|
36150
|
+
const raw = override ?? byDeployment.values().next().value;
|
|
36151
|
+
if (!raw) {
|
|
36152
|
+
return APP_URL;
|
|
36153
|
+
}
|
|
36154
|
+
let url;
|
|
36155
|
+
try {
|
|
36156
|
+
url = new URL(raw.trim());
|
|
36157
|
+
} catch {
|
|
36158
|
+
throw new SetupError(`${APP_URL_KEY} is not a valid URL: ${raw}`, [
|
|
36159
|
+
"Set it to the origin browsers use to reach Sim, e.g. https://sim.example.com"
|
|
36160
|
+
]);
|
|
36161
|
+
}
|
|
36162
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
36163
|
+
throw new SetupError(`${APP_URL_KEY} must be an http(s) URL: ${raw}`);
|
|
36164
|
+
}
|
|
36165
|
+
return url.origin;
|
|
36166
|
+
}
|
|
36167
|
+
async function probeDownload(downloadUrl, fetchImpl = fetch) {
|
|
36168
|
+
let response;
|
|
36169
|
+
try {
|
|
36170
|
+
response = await fetchImpl(downloadUrl, {
|
|
36171
|
+
redirect: "manual",
|
|
36172
|
+
signal: AbortSignal.timeout(PROBE_TIMEOUT_MS)
|
|
36173
|
+
});
|
|
36174
|
+
} catch (error) {
|
|
36175
|
+
return { status: "unreachable", error: getErrorMessage(error, "request failed") };
|
|
36176
|
+
}
|
|
36177
|
+
if (REDIRECT_STATUSES.has(response.status)) {
|
|
36178
|
+
const location = response.headers.get("location");
|
|
36179
|
+
if (!location)
|
|
36180
|
+
return { status: "unexpected", code: response.status };
|
|
36181
|
+
let name = location;
|
|
36182
|
+
try {
|
|
36183
|
+
name = decodeURIComponent(new URL(location).pathname.split("/").pop() ?? location);
|
|
36184
|
+
} catch {}
|
|
36185
|
+
return { status: "ok", installerUrl: location, installerName: sanitizeForTerminal(name) };
|
|
36186
|
+
}
|
|
36187
|
+
if (response.status === 404)
|
|
36188
|
+
return { status: "no-release" };
|
|
36189
|
+
if (response.status === 502)
|
|
36190
|
+
return { status: "feed-unavailable" };
|
|
36191
|
+
return { status: "unexpected", code: response.status };
|
|
36192
|
+
}
|
|
36193
|
+
function describeProbe(probe, appUrl) {
|
|
36194
|
+
switch (probe.status) {
|
|
36195
|
+
case "ok":
|
|
36196
|
+
return { headline: `${glyph.pass} Installer resolved: ${probe.installerName}`, hints: [] };
|
|
36197
|
+
case "no-release":
|
|
36198
|
+
return {
|
|
36199
|
+
headline: `${glyph.fail} This deployment reports no desktop release for its channel.`,
|
|
36200
|
+
hints: [
|
|
36201
|
+
"Stable desktop builds are published on GitHub releases of simstudioai/sim.",
|
|
36202
|
+
"A brand-new fork with no releases of its own will report this."
|
|
36203
|
+
]
|
|
36204
|
+
};
|
|
36205
|
+
case "feed-unavailable":
|
|
36206
|
+
return {
|
|
36207
|
+
headline: `${glyph.fail} The deployment could not reach the GitHub release feed.`,
|
|
36208
|
+
hints: [
|
|
36209
|
+
"The Sim server needs outbound access to api.github.com and github.com.",
|
|
36210
|
+
"Unauthenticated GitHub API calls are capped at 60/hour per IP — set GITHUB_TOKEN on the Sim server to raise it to 5000/hour."
|
|
36211
|
+
]
|
|
36212
|
+
};
|
|
36213
|
+
case "unreachable":
|
|
36214
|
+
return {
|
|
36215
|
+
headline: `${glyph.fail} Could not reach ${appUrl} — ${probe.error}`,
|
|
36216
|
+
hints: [
|
|
36217
|
+
`Check that Sim is running and reachable at ${appUrl} (npx sim-setup status).`,
|
|
36218
|
+
"Pass --url if this machine reaches Sim at a different address."
|
|
36219
|
+
]
|
|
36220
|
+
};
|
|
36221
|
+
case "unexpected":
|
|
36222
|
+
return { headline: `${glyph.fail} The download endpoint answered ${probe.code}.`, hints: [] };
|
|
36223
|
+
}
|
|
36224
|
+
}
|
|
36225
|
+
async function runDesktop(flags) {
|
|
36226
|
+
const appUrl = resolveDeploymentUrl(flags.url ? [] : discoverConfigurationSources(), flags.url);
|
|
36227
|
+
const downloadUrl = `${appUrl}${DOWNLOAD_PATH}`;
|
|
36228
|
+
log2.step(`Deployment: ${theme.accent(appUrl)}`);
|
|
36229
|
+
const spin = spinner2();
|
|
36230
|
+
spin.start("Resolving the desktop installer…");
|
|
36231
|
+
const [probe, feedOk] = await Promise.all([
|
|
36232
|
+
probeDownload(downloadUrl),
|
|
36233
|
+
httpHealth(`${appUrl}${FEED_PATH}`, PROBE_TIMEOUT_MS)
|
|
36234
|
+
]);
|
|
36235
|
+
const { headline, hints } = describeProbe(probe, appUrl);
|
|
36236
|
+
spin.stop(headline);
|
|
36237
|
+
if (probe.status !== "ok") {
|
|
36238
|
+
for (const hint of hints) {
|
|
36239
|
+
log2.info(hint);
|
|
36240
|
+
}
|
|
36241
|
+
outro2(theme.error("The desktop installer could not be resolved."));
|
|
36242
|
+
return 1;
|
|
36243
|
+
}
|
|
36244
|
+
if (!feedOk) {
|
|
36245
|
+
log2.warn(`${FEED_PATH} did not resolve — the app will install but will not auto-update from this deployment.`);
|
|
36246
|
+
}
|
|
36247
|
+
note2([
|
|
36248
|
+
`1. Download and install Sim:`,
|
|
36249
|
+
` ${theme.accent(downloadUrl)}`,
|
|
36250
|
+
"",
|
|
36251
|
+
`2. Open Sim, then choose ${theme.command("Sim → Server…")} in the menu bar.`,
|
|
36252
|
+
"",
|
|
36253
|
+
`3. Enter your server URL and press Connect:`,
|
|
36254
|
+
` ${theme.accent(appUrl)}`,
|
|
36255
|
+
"",
|
|
36256
|
+
theme.muted("Sim relaunches against your deployment and updates from it from then on."),
|
|
36257
|
+
theme.muted("The desktop app is macOS-only today; the web app works everywhere.")
|
|
36258
|
+
].join(`
|
|
36259
|
+
`), "Connect the desktop app");
|
|
36260
|
+
if (!flags.noOpen) {
|
|
36261
|
+
if (await confirm2({ message: "Download it now?", initialValue: true })) {
|
|
36262
|
+
openBrowser(downloadUrl);
|
|
36263
|
+
}
|
|
36264
|
+
}
|
|
36265
|
+
outro2(theme.accent("Ready."));
|
|
36266
|
+
return 0;
|
|
36267
|
+
}
|
|
36268
|
+
var DOWNLOAD_PATH = "/api/desktop/update/download", FEED_PATH = "/api/desktop/update/latest-mac.yml", PROBE_TIMEOUT_MS = 15000, REDIRECT_STATUSES, APP_URL_KEY = "NEXT_PUBLIC_APP_URL", MAX_INSTALLER_NAME = 120;
|
|
36269
|
+
var init_desktop = __esm(() => {
|
|
36270
|
+
init_string();
|
|
36271
|
+
init_cli_auth();
|
|
36272
|
+
init_configuration_sources();
|
|
36273
|
+
init_errors();
|
|
36274
|
+
init_probes();
|
|
36275
|
+
init_prompter();
|
|
36276
|
+
init_theme();
|
|
36277
|
+
init_urls();
|
|
36278
|
+
REDIRECT_STATUSES = new Set([301, 302, 307, 308]);
|
|
36279
|
+
});
|
|
36280
|
+
|
|
36109
36281
|
// src/doctor.ts
|
|
36110
36282
|
var exports_doctor = {};
|
|
36111
36283
|
__export(exports_doctor, {
|
|
@@ -36367,12 +36539,6 @@ var init_docker = __esm(() => {
|
|
|
36367
36539
|
APP_DIRS = ["/Applications", join(homedir(), "Applications")];
|
|
36368
36540
|
});
|
|
36369
36541
|
|
|
36370
|
-
// src/urls.ts
|
|
36371
|
-
var APP_URL = "http://localhost:3000", APP_SIGNUP_URL;
|
|
36372
|
-
var init_urls = __esm(() => {
|
|
36373
|
-
APP_SIGNUP_URL = `${APP_URL}/signup`;
|
|
36374
|
-
});
|
|
36375
|
-
|
|
36376
36542
|
// src/modes/k8s.ts
|
|
36377
36543
|
import { spawn, spawnSync as spawnSync6 } from "node:child_process";
|
|
36378
36544
|
function run(command, args, failMessage, input) {
|
|
@@ -38260,24 +38426,31 @@ function oneFlag(args, flag) {
|
|
|
38260
38426
|
fail(`${flag} may only be provided once`);
|
|
38261
38427
|
return count === 1;
|
|
38262
38428
|
}
|
|
38263
|
-
function
|
|
38264
|
-
let
|
|
38429
|
+
function parseValueOption(args, flag, requirement) {
|
|
38430
|
+
let value;
|
|
38265
38431
|
const remaining = [];
|
|
38432
|
+
const prefix = `${flag}=`;
|
|
38266
38433
|
for (let index = 0;index < args.length; index += 1) {
|
|
38267
38434
|
const arg = args[index];
|
|
38268
|
-
if (arg !==
|
|
38435
|
+
if (arg !== flag && !arg.startsWith(prefix)) {
|
|
38269
38436
|
remaining.push(arg);
|
|
38270
38437
|
continue;
|
|
38271
38438
|
}
|
|
38272
|
-
if (
|
|
38273
|
-
fail(
|
|
38274
|
-
const
|
|
38275
|
-
if (
|
|
38276
|
-
fail(
|
|
38277
|
-
|
|
38278
|
-
|
|
38439
|
+
if (value !== undefined)
|
|
38440
|
+
fail(`${flag} may only be provided once`);
|
|
38441
|
+
const candidate = arg === flag ? args[++index] : arg.slice(prefix.length);
|
|
38442
|
+
if (!candidate || candidate.startsWith("-"))
|
|
38443
|
+
fail(requirement);
|
|
38444
|
+
value = candidate;
|
|
38445
|
+
}
|
|
38446
|
+
return { value, remaining };
|
|
38447
|
+
}
|
|
38448
|
+
function parseMode(args) {
|
|
38449
|
+
const { value, remaining } = parseValueOption(args, "--mode", "--mode requires a value");
|
|
38450
|
+
if (value !== undefined && value !== "compose" && value !== "dev" && value !== "k8s") {
|
|
38451
|
+
fail(`invalid --mode "${value}" — expected compose, dev, or k8s`);
|
|
38279
38452
|
}
|
|
38280
|
-
return { mode, remaining };
|
|
38453
|
+
return { mode: value, remaining };
|
|
38281
38454
|
}
|
|
38282
38455
|
function expectNoArguments(command, args) {
|
|
38283
38456
|
if (args.length > 0)
|
|
@@ -38320,6 +38493,13 @@ function parseCore(args, helpRequested) {
|
|
|
38320
38493
|
}
|
|
38321
38494
|
return { kind: "add", feature, args: featureArgs };
|
|
38322
38495
|
}
|
|
38496
|
+
if (command === "desktop") {
|
|
38497
|
+
const noOpen = oneFlag(commandArgs, "--no-open");
|
|
38498
|
+
const { value: url, remaining } = parseValueOption(commandArgs.filter((arg) => arg !== "--no-open"), "--url", "--url requires a deployment URL");
|
|
38499
|
+
if (remaining.length > 0)
|
|
38500
|
+
fail(`Unknown desktop option: ${remaining[0]}`);
|
|
38501
|
+
return { kind: "desktop", noOpen, ...url ? { url } : {} };
|
|
38502
|
+
}
|
|
38323
38503
|
if (command === "doctor") {
|
|
38324
38504
|
const fix = oneFlag(commandArgs, "--fix");
|
|
38325
38505
|
const json = oneFlag(commandArgs, "--json");
|
|
@@ -38364,6 +38544,7 @@ var USAGE = `Usage:
|
|
|
38364
38544
|
sim-setup [--quick] [--dir <path>] [--mode compose|dev|k8s]
|
|
38365
38545
|
sim-setup config show configured capabilities and integrations
|
|
38366
38546
|
sim-setup add <feature> configure ${SETUP_FEATURES2}
|
|
38547
|
+
sim-setup desktop [--url <url>] install the macOS desktop app against this deployment
|
|
38367
38548
|
sim-setup doctor [--fix] [--json] check your setup
|
|
38368
38549
|
sim-setup start | stop | restart bring your install up / down / cycle
|
|
38369
38550
|
sim-setup update pull/rebuild and apply Compose images
|
|
@@ -38394,6 +38575,11 @@ async function main() {
|
|
|
38394
38575
|
await runFeatureSetup2(invocation.feature, invocation.args);
|
|
38395
38576
|
return;
|
|
38396
38577
|
}
|
|
38578
|
+
if (invocation.kind === "desktop") {
|
|
38579
|
+
const { runDesktop: runDesktop2 } = await Promise.resolve().then(() => (init_desktop(), exports_desktop));
|
|
38580
|
+
process.exitCode = await runDesktop2({ url: invocation.url, noOpen: invocation.noOpen });
|
|
38581
|
+
return;
|
|
38582
|
+
}
|
|
38397
38583
|
if (invocation.kind === "doctor") {
|
|
38398
38584
|
const { runDoctor: runDoctor2 } = await Promise.resolve().then(() => (init_doctor(), exports_doctor));
|
|
38399
38585
|
process.exitCode = await runDoctor2({ fix: invocation.fix, json: invocation.json });
|