@khal-os/cli 1.0.27 → 1.0.39

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.
Files changed (2) hide show
  1. package/dist/index.js +143 -14
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -14262,17 +14262,17 @@ function detectArch() {
14262
14262
  return "arm64";
14263
14263
  return "x64";
14264
14264
  }
14265
- function getNatsUrl(version, platform, arch) {
14266
- const os2 = platform === "windows" ? "windows" : platform;
14265
+ function getNatsUrl(version, platform2, arch) {
14266
+ const os2 = platform2 === "windows" ? "windows" : platform2;
14267
14267
  const a = arch === "arm64" ? "arm64" : "amd64";
14268
- const ext = platform === "windows" ? "zip" : "tar.gz";
14268
+ const ext = platform2 === "windows" ? "zip" : "tar.gz";
14269
14269
  return `https://github.com/nats-io/nats-server/releases/download/v${version}/nats-server-v${version}-${os2}-${a}.${ext}`;
14270
14270
  }
14271
- function getBunUrl(version, platform, arch) {
14272
- const os2 = platform === "windows" ? "windows" : platform;
14271
+ function getBunUrl(version, platform2, arch) {
14272
+ const os2 = platform2 === "windows" ? "windows" : platform2;
14273
14273
  const a = arch === "arm64" ? "aarch64" : "x64";
14274
14274
  const profile = "baseline";
14275
- const ext = platform === "windows" ? "zip" : "zip";
14275
+ const ext = platform2 === "windows" ? "zip" : "zip";
14276
14276
  return `https://github.com/oven-sh/bun/releases/download/bun-v${version}/bun-${os2}-${a}-${profile}.${ext}`;
14277
14277
  }
14278
14278
  function followRedirects(url, maxRedirects = 5) {
@@ -14508,10 +14508,10 @@ var init_local = __esm(() => {
14508
14508
  }
14509
14509
  async ensureDeps() {
14510
14510
  const binDir = join4(this.dataDir, "bin");
14511
- const platform = detectPlatform();
14511
+ const platform2 = detectPlatform();
14512
14512
  const arch = detectArch();
14513
14513
  const emitter = (event) => this.emit(event);
14514
- this.natsBin = await ensureBinary("nats-server", NATS_VERSION, binDir, getNatsUrl(NATS_VERSION, platform, arch), emitter);
14514
+ this.natsBin = await ensureBinary("nats-server", NATS_VERSION, binDir, getNatsUrl(NATS_VERSION, platform2, arch), emitter);
14515
14515
  }
14516
14516
  async depsReady() {
14517
14517
  const binDir = join4(this.dataDir, "bin");
@@ -14892,9 +14892,9 @@ function isInCluster() {
14892
14892
  _inClusterCached = existsSync5(IN_CLUSTER_TOKEN_PATH) && !!process.env.KUBERNETES_SERVICE_HOST;
14893
14893
  return _inClusterCached;
14894
14894
  }
14895
- var exec, IN_CLUSTER_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token", _inClusterCached, DEFAULT_KUBECONFIG = "/etc/rancher/k3s/k3s.yaml", DEFAULT_NATS_URL = "nats://nats:4222", DEFAULT_RESOURCES, SANDBOX_RUNTIME_CLASS = "sysbox-runc", SANDBOX_PVC_SIZE = "20Gi", SANDBOX_STORAGE_CLASS = "local-path", SANDBOX_SSH_PORT = 22, SANDBOX_NAMESPACE = "khal-sandbox", SANDBOX_HELM_CHART, SANDBOX_RESOURCES, KubernetesRuntime;
14895
+ var exec2, IN_CLUSTER_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token", _inClusterCached, DEFAULT_KUBECONFIG = "/etc/rancher/k3s/k3s.yaml", DEFAULT_NATS_URL = "nats://nats:4222", DEFAULT_RESOURCES, SANDBOX_RUNTIME_CLASS = "sysbox-runc", SANDBOX_PVC_SIZE = "20Gi", SANDBOX_STORAGE_CLASS = "local-path", SANDBOX_SSH_PORT = 22, SANDBOX_NAMESPACE = "khal-sandbox", SANDBOX_HELM_CHART, SANDBOX_RESOURCES, KubernetesRuntime;
14896
14896
  var init_kubernetes = __esm(() => {
14897
- exec = promisify(execFile);
14897
+ exec2 = promisify(execFile);
14898
14898
  DEFAULT_RESOURCES = {
14899
14899
  requests: { cpu: "100m", memory: "256Mi" },
14900
14900
  limits: { cpu: "1", memory: "1Gi" }
@@ -14978,7 +14978,7 @@ var init_kubernetes = __esm(() => {
14978
14978
  }
14979
14979
  if (this.sandboxMode) {
14980
14980
  try {
14981
- await exec("helm", ["version", "--short"], { timeout: 1e4 });
14981
+ await exec2("helm", ["version", "--short"], { timeout: 1e4 });
14982
14982
  this.emit({ type: "dep:ready", name: "helm", path: "helm" });
14983
14983
  } catch {
14984
14984
  throw new Error("helm not found. Required for sandbox deployment.");
@@ -15062,7 +15062,7 @@ var init_kubernetes = __esm(() => {
15062
15062
  message: `helm ${helmArgs.join(" ")}`
15063
15063
  });
15064
15064
  try {
15065
- await exec("helm", helmArgs, { timeout: 240000 });
15065
+ await exec2("helm", helmArgs, { timeout: 240000 });
15066
15066
  } catch (err) {
15067
15067
  const msg = err instanceof Error ? err.message : String(err);
15068
15068
  throw new Error(`helm upgrade --install ${this.helmRelease} failed: ${msg}`);
@@ -15138,7 +15138,7 @@ var init_kubernetes = __esm(() => {
15138
15138
  }
15139
15139
  async stopSandbox() {
15140
15140
  try {
15141
- await exec("helm", [...this.kubeconfigArgs(), "uninstall", this.helmRelease, "--namespace", this.namespace, "--wait"], { timeout: 60000 });
15141
+ await exec2("helm", [...this.kubeconfigArgs(), "uninstall", this.helmRelease, "--namespace", this.namespace, "--wait"], { timeout: 60000 });
15142
15142
  this.emit({
15143
15143
  type: "log",
15144
15144
  source: "kubernetes",
@@ -15300,7 +15300,7 @@ var init_kubernetes = __esm(() => {
15300
15300
  return this.kubeconfig ? ["--kubeconfig", this.kubeconfig] : [];
15301
15301
  }
15302
15302
  async kubectl(args) {
15303
- const { stdout } = await exec("kubectl", [...this.kubeconfigArgs(), ...args], {
15303
+ const { stdout } = await exec2("kubectl", [...this.kubeconfigArgs(), ...args], {
15304
15304
  timeout: 30000
15305
15305
  });
15306
15306
  return stdout;
@@ -20307,6 +20307,134 @@ ${source_default.bold("Leaderboard")}
20307
20307
  console.log("");
20308
20308
  });
20309
20309
 
20310
+ // src/commands/auth.ts
20311
+ import { exec } from "node:child_process";
20312
+ import { platform } from "node:os";
20313
+ import { setTimeout as wait } from "node:timers/promises";
20314
+ var PLATFORM_DEFAULT = process.env.KHAL_PLATFORM_URL ?? "https://platform.khal.ai";
20315
+ function openBrowser(url) {
20316
+ const cmd = platform() === "darwin" ? `open "${url}"` : platform() === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
20317
+ exec(cmd, () => {});
20318
+ }
20319
+ async function fetchJson(url, init) {
20320
+ const resp = await fetch(url, init);
20321
+ const body = await resp.json().catch(() => ({}));
20322
+ if (!resp.ok || body.error) {
20323
+ throw new Error(body.error ?? `HTTP ${resp.status}`);
20324
+ }
20325
+ return body;
20326
+ }
20327
+ async function pollForConnection(platformUrl, userId, provider, timeoutMs) {
20328
+ const start = Date.now();
20329
+ while (Date.now() - start < timeoutMs) {
20330
+ try {
20331
+ const list = await fetchJson(`${platformUrl}/v1/integrations/list?userId=${encodeURIComponent(userId)}`);
20332
+ const match = (list.integrations ?? []).find((i) => i.provider === provider && !i.revoked);
20333
+ if (match)
20334
+ return match;
20335
+ } catch {}
20336
+ await wait(2000);
20337
+ }
20338
+ return null;
20339
+ }
20340
+ var authCommand = new Command("auth").description("Connect third-party accounts (GitHub for private pack installs)");
20341
+ authCommand.command("github").description("Authorize Khal OS to read your GitHub repos for pack installs").option("-u, --user-id <id>", "User ID (defaults to KHAL_USER_ID env)").option("-p, --platform <url>", `Platform URL (default: ${PLATFORM_DEFAULT})`).option("--no-open", "Don't auto-open the browser; print the URL only").option("--timeout <seconds>", "Polling timeout in seconds", "180").action(async (opts) => {
20342
+ const userId = opts.userId ?? process.env.KHAL_USER_ID;
20343
+ if (!userId) {
20344
+ console.error(source_default.red("User ID required. Pass --user-id <id> or set KHAL_USER_ID. Use the same id you'll pass to install workflows."));
20345
+ process.exit(1);
20346
+ }
20347
+ const platformUrl = opts.platform ?? PLATFORM_DEFAULT;
20348
+ const timeoutSec = Number.parseInt(opts.timeout ?? "180", 10);
20349
+ console.log(`${source_default.dim("platform:")} ${platformUrl}`);
20350
+ console.log(`${source_default.dim("user-id:")} ${userId}`);
20351
+ console.log();
20352
+ console.log(source_default.yellow("Requesting authorize URL..."));
20353
+ let authResp;
20354
+ try {
20355
+ authResp = await fetchJson(`${platformUrl}/v1/integrations/github/authorize`, {
20356
+ method: "POST",
20357
+ headers: { "Content-Type": "application/json" },
20358
+ body: JSON.stringify({ userId })
20359
+ });
20360
+ } catch (err) {
20361
+ const msg = err instanceof Error ? err.message : String(err);
20362
+ console.error(source_default.red(`Authorize request failed: ${msg}`));
20363
+ console.error(source_default.dim("Is the platform deployed with GITHUB_OAUTH_CLIENT_ID set?"));
20364
+ process.exit(1);
20365
+ return;
20366
+ }
20367
+ if (!authResp.authorizeUrl) {
20368
+ console.error(source_default.red("Platform did not return an authorize URL."));
20369
+ process.exit(1);
20370
+ return;
20371
+ }
20372
+ console.log();
20373
+ console.log(source_default.bold("Open this URL in your browser to authorize:"));
20374
+ console.log(` ${source_default.cyan(authResp.authorizeUrl)}`);
20375
+ console.log();
20376
+ if (opts.open !== false) {
20377
+ openBrowser(authResp.authorizeUrl);
20378
+ console.log(source_default.dim("(opening in your default browser)"));
20379
+ console.log();
20380
+ }
20381
+ console.log(`${source_default.yellow("Waiting for callback...")} (timeout ${timeoutSec}s)`);
20382
+ const result = await pollForConnection(platformUrl, userId, "github", timeoutSec * 1000);
20383
+ if (!result) {
20384
+ console.error();
20385
+ console.error(source_default.red("Timed out waiting for OAuth callback to complete."));
20386
+ console.error(source_default.dim("You can re-run `khal-os auth github` if you ran out of time, or if approval failed."));
20387
+ process.exit(1);
20388
+ return;
20389
+ }
20390
+ console.log();
20391
+ console.log(`${source_default.green("✓")} GitHub connected as ${source_default.bold(result.providerAccountLogin ?? "?")}`);
20392
+ console.log(`${source_default.dim(" scopes:")} ${result.scopes ?? "(none)"}`);
20393
+ console.log(`${source_default.dim(" connected:")} ${result.connectedAt}`);
20394
+ console.log();
20395
+ console.log(source_default.dim("Token is encrypted at rest on the platform; CLI never sees it."));
20396
+ });
20397
+ authCommand.command("list").description("List your connected provider integrations").option("-u, --user-id <id>", "User ID (defaults to KHAL_USER_ID env)").option("-p, --platform <url>", `Platform URL (default: ${PLATFORM_DEFAULT})`).action(async (opts) => {
20398
+ const userId = opts.userId ?? process.env.KHAL_USER_ID;
20399
+ if (!userId) {
20400
+ console.error(source_default.red("User ID required."));
20401
+ process.exit(1);
20402
+ }
20403
+ const platformUrl = opts.platform ?? PLATFORM_DEFAULT;
20404
+ try {
20405
+ const list = await fetchJson(`${platformUrl}/v1/integrations/list?userId=${encodeURIComponent(userId)}`);
20406
+ const items = list.integrations ?? [];
20407
+ if (items.length === 0) {
20408
+ console.log(source_default.dim("No integrations connected."));
20409
+ return;
20410
+ }
20411
+ for (const it of items) {
20412
+ const status = it.revoked ? source_default.red("revoked") : source_default.green("active");
20413
+ console.log(` ${it.provider} ${source_default.bold(it.providerAccountLogin ?? "?")} ${status} ${source_default.dim(it.scopes ?? "")}`);
20414
+ }
20415
+ } catch (err) {
20416
+ const msg = err instanceof Error ? err.message : String(err);
20417
+ console.error(source_default.red(`Failed to list integrations: ${msg}`));
20418
+ process.exit(1);
20419
+ }
20420
+ });
20421
+ authCommand.command("disconnect <provider>").description("Disconnect a provider (soft-revoke; token row stays for audit)").option("-u, --user-id <id>", "User ID (defaults to KHAL_USER_ID env)").option("-p, --platform <url>", `Platform URL (default: ${PLATFORM_DEFAULT})`).action(async (provider, opts) => {
20422
+ const userId = opts.userId ?? process.env.KHAL_USER_ID;
20423
+ if (!userId) {
20424
+ console.error(source_default.red("User ID required."));
20425
+ process.exit(1);
20426
+ }
20427
+ const platformUrl = opts.platform ?? PLATFORM_DEFAULT;
20428
+ try {
20429
+ await fetchJson(`${platformUrl}/v1/integrations/${encodeURIComponent(provider)}?userId=${encodeURIComponent(userId)}`, { method: "DELETE" });
20430
+ console.log(`${source_default.green("✓")} Disconnected ${provider}`);
20431
+ } catch (err) {
20432
+ const msg = err instanceof Error ? err.message : String(err);
20433
+ console.error(source_default.red(`Disconnect failed: ${msg}`));
20434
+ process.exit(1);
20435
+ }
20436
+ });
20437
+
20310
20438
  // src/commands/deploy.ts
20311
20439
  import { readFileSync as readFileSync3 } from "node:fs";
20312
20440
  import { resolve } from "node:path";
@@ -23915,6 +24043,7 @@ ${source_default.bold("◆ Khal OS")}
23915
24043
  // src/index.ts
23916
24044
  var program2 = new Command().name("khal-os").description("Khal OS — develop, deploy, and observe").version(KHALOS_VERSION).option("-i, --instance <url>", "KhalOS instance URL (overrides KHAL_INSTANCE env and config)");
23917
24045
  program2.addCommand(appCommand);
24046
+ program2.addCommand(authCommand);
23918
24047
  program2.addCommand(devCommand);
23919
24048
  program2.addCommand(startCommand);
23920
24049
  program2.addCommand(stopCommand);
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@khal-os/cli",
3
- "version": "1.0.27",
3
+ "version": "1.0.39",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "bin": {
10
- "khal-os": "./dist/cli.js"
10
+ "khal-os": "./dist/index.js"
11
11
  },
12
12
  "files": [
13
13
  "dist",