import { createRequire } from 'node:module';const require = createRequire(import.meta.url);

// src/base.ts
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
import os from "node:os";
import { dirname as dirname2, join as join2 } from "node:path";
import readline from "node:readline";
import { Args, Command, Flags as Flags2, settings } from "@oclif/core";
import chalk3 from "chalk";
import * as fs from "fs-extra";
import yaml from "js-yaml";
import pkgUp2 from "pkg-up";
import { LaunchctlRunner, PhoenixDaemon, SystemDaemon, SystemctlRunner } from "@dxos/agent";
import { Client, Config, fromAgent } from "@dxos/client";
import { createEdgeIdentity } from "@dxos/client/edge";
import { DX_CONFIG, DX_DATA, DX_RUNTIME, ENV_DX_CONFIG, ENV_DX_NO_AGENT, ENV_DX_PROFILE, ENV_DX_PROFILE_DEFAULT, getProfilePath } from "@dxos/client-protocol";
import { Remote } from "@dxos/config";
import { raise } from "@dxos/debug";
import { invariant as invariant3 } from "@dxos/invariant";
import { LogLevel, createFileProcessor, log as log2, parseFilter } from "@dxos/log";
import { getObservabilityState, initializeNodeObservability, showObservabilityBanner } from "@dxos/observability";
import { SpaceState as SpaceState2 } from "@dxos/protocols/proto/dxos/client/services";

// src/errors.ts
var FriendlyError = class extends Error {
  get hint() {
    return void 0;
  }
};
var SpaceTimeoutError = class extends FriendlyError {
  get hint() {
    return "Increase timeout.";
  }
  constructor(timeout) {
    super(`Timeout waiting for space to load (${timeout.toLocaleString()}ms)`);
  }
};
var IdentityWaitTimeoutError = class extends FriendlyError {
  constructor() {
    super("Timeout waiting for identity.");
  }
};
var ClientInitializationError = class extends FriendlyError {
  constructor(message, error) {
    super(`Error while initializing client: ${message}: ${error.message}`, {
      cause: error
    });
  }
};
var AgentAlreadyRunningError = class extends FriendlyError {
  get hint() {
    return "Check running agents (dx agent list) or daemon process.";
  }
  constructor() {
    super("Agent is already running.");
  }
};

// src/util/agents.ts
import { ux } from "@oclif/core";
import { formatDistance } from "date-fns";

// src/util/table.ts
import { Flags } from "@oclif/core";
import chalk from "chalk";
import Table from "cli-table3";
var TABLE_FLAGS = {
  extended: Flags.boolean({
    description: "Show extended table columns."
  })
};
var table = (data = [], header, options = {
  extended: false
}) => {
  const f = ([_, h]) => !h.extended || options.extended;
  const t = new Table({
    head: Object.entries(header).filter(f).map(([key, { header: header2 }]) => header2 ?? key),
    colWidths: Object.entries(header).filter(f).map(([_, { width = null }]) => width),
    style: {
      compact: true,
      head: [
        "green"
      ]
    }
  });
  t.push(...data.map((row) => Object.entries(header).filter(f).map(([key, { color, primary, truncate, get }]) => {
    let value = get ? get(row) : row[key] ?? "";
    if (truncate) {
      value = value.slice(0, 8);
    }
    const c = color ?? (primary && chalk.blue);
    return c ? c(value) : value;
  })));
  return t.toString();
};

// src/util/agents.ts
var printAgents = (daemons, flags = {}) => {
  ux.stdout(table(daemons, {
    profile: {
      header: "profile"
    },
    pid: {
      header: "process"
    },
    running: {
      header: "running"
    },
    locked: {
      header: "locked"
    },
    restarts: {
      header: "restarts"
    },
    started: {
      header: "uptime",
      get: (row) => {
        if (!row.running) {
          return "stopped";
        }
        if (!row.started) {
          return "Null";
        }
        return formatDistance(/* @__PURE__ */ new Date(), new Date(row.started));
      }
    },
    logFile: {
      header: "logFile",
      extended: true
    }
  }, flags));
};

// src/util/keys.ts
import { truncateKey } from "@dxos/debug";
var matchKeys = (key, str) => str.filter((str2) => key.toHex().startsWith(str2)).length > 0;
var maybeTruncateKey = (key, truncate = false) => truncate ? truncateKey(key) : key.toHex();

// src/util/credentials.ts
var mapCredentials = (credentials, truncateKeys = false) => {
  return credentials.map((credential) => ({
    id: maybeTruncateKey(credential.id, truncateKeys),
    issuer: maybeTruncateKey(credential.issuer, truncateKeys),
    subject: maybeTruncateKey(credential.subject.id, truncateKeys),
    type: credential.subject.assertion["@type"],
    assertion: credential.subject.assertion
  }));
};
var printCredentials = (credentials, flags = {}) => {
  console.log(credentials);
};

// src/util/devices.ts
import { ux as ux2 } from "@oclif/core";
import { Device, DeviceKind, DeviceType } from "@dxos/client/halo";
var mapDevices = (devices, truncateKeys = false) => {
  return devices.map((device) => ({
    label: device.profile?.label,
    type: device.profile?.type ? DeviceType[device.profile?.type] : "UNKNOWN",
    key: maybeTruncateKey(device.deviceKey, truncateKeys),
    kind: DeviceKind[device.kind],
    platform: device.profile?.platform,
    platformVersion: device.profile?.platformVersion,
    architecture: device.profile?.architecture,
    os: device.profile?.os,
    osVersion: device.profile?.osVersion,
    presence: device?.kind === DeviceKind.CURRENT ? "THIS DEVICE" : Device.PresenceState[device.presence]
  }));
};
var printDevices = (devices, flags = {}) => {
  ux2.stdout(table(mapDevices(devices, true), {
    key: {
      header: "key"
    },
    label: {
      header: "label"
    },
    presence: {
      header: "presence"
    },
    type: {
      header: "type"
    },
    kind: {
      header: "kind"
    },
    platform: {
      header: "platform"
    },
    platformVersion: {
      header: "platformVersion"
    },
    architecture: {
      header: "architecture"
    },
    os: {
      header: "os"
    },
    osVersion: {
      header: "osVersion"
    }
  }, flags));
};

// src/util/invitations.ts
import { Event, Trigger } from "@dxos/async";
import { Invitation } from "@dxos/client/invitations";
import { invariant } from "@dxos/invariant";
import { AlreadyJoinedError } from "@dxos/protocols";
var __dxlog_file = "/__w/dxos/dxos/packages/devtools/cli-base/src/util/invitations.ts";
var hostInvitation = async ({ observable, callbacks, peersNumber = 1, waitForSuccess = true }) => {
  const invitationEvent = new Event();
  const connectingEvent = new Event();
  const done = invitationEvent.waitForCount(peersNumber);
  const unsubscribeHandle = observable.subscribe(async (invitation2) => {
    switch (invitation2.state) {
      case Invitation.State.CONNECTING: {
        await callbacks?.onConnecting?.(invitation2);
        connectingEvent.emit(invitation2);
        break;
      }
      case Invitation.State.SUCCESS: {
        await callbacks?.onSuccess?.(invitation2);
        invitationEvent.emit(invitation2);
        break;
      }
    }
  }, (err) => {
    throw err;
  });
  let invitation;
  if (waitForSuccess) {
    invitation = await done;
  } else {
    invitation = await connectingEvent.waitForCount(1);
  }
  unsubscribeHandle.unsubscribe();
  return invitation;
};
var acceptInvitation = async ({ observable, callbacks }) => {
  const done = new Trigger();
  const unsubscribeHandle = observable.subscribe(async (invitation2) => {
    switch (invitation2.state) {
      case Invitation.State.CONNECTING: {
        await callbacks?.onConnecting?.(invitation2);
        break;
      }
      case Invitation.State.READY_FOR_AUTHENTICATION: {
        if (invitation2.authMethod === Invitation.AuthMethod.SHARED_SECRET) {
          const callbackResult = await callbacks?.onReadyForAuth?.(invitation2);
          const code = invitation2.authCode ?? callbackResult;
          invariant(code, "No code provided", {
            F: __dxlog_file,
            L: 90,
            S: void 0,
            A: [
              "code",
              "'No code provided'"
            ]
          });
          await observable.authenticate(code);
        }
        break;
      }
      case Invitation.State.SUCCESS: {
        await callbacks?.onSuccess?.(invitation2);
        done.wake(invitation2);
        break;
      }
    }
  }, (err) => {
    if (err instanceof AlreadyJoinedError) {
      done.wake(observable.get());
      return;
    }
    throw err;
  });
  const invitation = await done.wait();
  unsubscribeHandle.unsubscribe();
  return invitation;
};

// src/util/ipfs.ts
import { invariant as invariant2 } from "@dxos/invariant";
import { log } from "@dxos/log";
var __dxlog_file2 = "/__w/dxos/dxos/packages/devtools/cli-base/src/util/ipfs.ts";
var importESM = new Function("modulePath", "return import(modulePath)");
var createIpfsClient = async (config, timeout) => {
  const { create } = await importESM("kubo-rpc-client");
  const serverAuthSecret = process.env.IPFS_API_SECRET ?? config?.get("runtime.services.ipfs.serverAuthSecret");
  let authorizationHeader;
  if (serverAuthSecret) {
    const splitSecret = serverAuthSecret.split(":");
    switch (splitSecret[0]) {
      case "basic":
        authorizationHeader = "Basic " + Buffer.from(splitSecret[1] + ":" + splitSecret[2]).toString("base64");
        break;
      case "bearer":
        authorizationHeader = "Bearer " + splitSecret[1];
        break;
      default:
        throw new Error(`Unsupported authType: ${splitSecret[0]}`);
    }
  }
  const server = config?.get("runtime.services.ipfs.server");
  invariant2(server, "Missing IPFS Server.", {
    F: __dxlog_file2,
    L: 33,
    S: void 0,
    A: [
      "server",
      "'Missing IPFS Server.'"
    ]
  });
  log("connecting to IPFS server", {
    server
  }, {
    F: __dxlog_file2,
    L: 34,
    S: void 0,
    C: (f, a) => f(...a)
  });
  return create({
    url: server,
    timeout: timeout || "1m",
    ...authorizationHeader ? {
      headers: {
        authorization: authorizationHeader
      }
    } : {}
  });
};

// src/util/json.ts
import chalk2 from "chalk";
var stringify = (value) => {
  if (typeof value === "string") {
    if (value.length > 32 && value.match(/^\w+$/)) {
      return `"${chalk2.green(value.slice(0, 8))}"`;
    }
    return `"${chalk2.green(value)}"`;
  } else if (Array.isArray(value)) {
    return chalk2.gray("[") + value.map((value2) => stringify(value2)) + chalk2.gray("]");
  } else if (typeof value === "object") {
    return chalk2.gray("{") + Object.entries(value).map(([key, value2]) => `${chalk2.blue(key)}: ${stringify(value2)}`).join(", ") + chalk2.gray("}");
  } else if (value === true) {
    return chalk2.green("true");
  } else if (value === false) {
    return chalk2.red("false");
  } else {
    return String(chalk2.cyan(value));
  }
};

// src/util/spaces.ts
import { ux as ux3 } from "@oclif/core";
import { asyncTimeout } from "@dxos/async";
import { SpaceMember, SpaceState } from "@dxos/client/echo";
import { truncateKey as truncateKey2 } from "@dxos/debug";

// src/defaults.ts
var SPACE_WAIT_TIMEOUT = 6e4;
var IDENTITY_WAIT_TIMEOUT = 1e3;

// src/util/spaces.ts
var asyncImport = (module) => import(module);
var selectSpace = async (spaces) => {
  const inquirer = (await asyncImport("inquirer")).default;
  const { key } = await inquirer.prompt([
    {
      name: "key",
      type: "list",
      message: "Select a space:",
      choices: await Promise.all(spaces.map(async (space) => {
        await waitForSpace(space);
        return {
          name: `[${truncateKey2(space.key)}] ${space.properties.name ?? ""}`,
          value: space.key
        };
      }))
    }
  ]);
  return key;
};
var waitForSpace = async (space, timeout = SPACE_WAIT_TIMEOUT, exceptionHandler) => {
  try {
    await asyncTimeout(space.waitUntilReady(), timeout, new SpaceTimeoutError(timeout));
  } catch (err) {
    if (exceptionHandler) {
      exceptionHandler(err);
    } else {
      throw err;
    }
  }
};
var mapSpaces = async (spaces, options = {
  verbose: false,
  truncateKeys: false
}) => {
  return await Promise.all(spaces.map(async (space) => {
    const { open, ready } = space.internal.data.metrics ?? {};
    const startup = open && ready && ready.getTime() - open.getTime();
    const pipeline = space.internal.data.pipeline;
    const epoch = pipeline?.currentEpoch?.subject.assertion.number;
    const syncState = aggregateSyncState(await space.db.coreDatabase.getSyncState());
    return {
      id: space.id,
      state: SpaceState[space.state.get()],
      name: space.state.get() === SpaceState.SPACE_READY ? space.properties.name : "loading...",
      members: space.members.get().length,
      objects: space.db.coreDatabase.getAllObjectIds().length,
      key: maybeTruncateKey(space.key, options.truncateKeys),
      epoch,
      startup,
      automergeRoot: space.internal.data.pipeline?.spaceRootUrl,
      // appliedEpoch,
      syncState: `${syncState.count} ${getSyncIndicator(syncState.up, syncState.down)} (${syncState.peers} peers)`
    };
  }));
};
var printSpaces = async (spaces, flags = {}) => {
  ux3.stdout(table(await mapSpaces(spaces, {
    ...flags,
    truncateKeys: true
  }), {
    id: {
      header: "id",
      width: 8
    },
    state: {
      header: "state"
    },
    name: {
      header: "name"
    },
    members: {
      header: "members"
    },
    objects: {
      header: "objects"
    },
    syncState: {
      header: "sync"
    },
    key: {
      header: "key"
    },
    epoch: {
      header: "epoch"
    },
    // appliedEpoch: {
    //   header: 'Applied Epoch',
    // },
    startup: {
      header: "startup",
      extended: true
    },
    automergeRoot: {
      header: "automerge root doc",
      extended: true
    }
  }, flags));
};
var mapMembers = (members, truncateKeys = false) => {
  return members.map((member) => ({
    key: maybeTruncateKey(member.identity.identityKey, truncateKeys),
    name: member.identity.profile?.displayName,
    presence: member.presence === SpaceMember.PresenceState.ONLINE ? "Online" : "Offline"
  }));
};
var printMembers = (members, flags = {}) => {
  ux3.stdout(table(mapMembers(members, true), {
    key: {
      header: "identity key"
    },
    name: {
      header: "display name"
    },
    presence: {
      header: "presence"
    }
  }, flags));
};
var aggregateSyncState = (syncState) => {
  const peers = Object.keys(syncState.peers ?? {}).length;
  const missingOnLocal = Math.max(...Object.values(syncState.peers ?? {}).map((peer) => peer.missingOnLocal), 0);
  const missingOnRemote = Math.max(...Object.values(syncState.peers ?? {}).map((peer) => peer.missingOnRemote), 0);
  const differentDocuments = Math.max(...Object.values(syncState.peers ?? {}).map((peer) => peer.differentDocuments), 0);
  return {
    count: missingOnLocal + missingOnRemote + differentDocuments,
    peers,
    up: missingOnRemote > 0 || differentDocuments > 0,
    down: missingOnLocal > 0 || differentDocuments > 0
  };
};
var getSyncIndicator = (up, down) => {
  if (up) {
    if (down) {
      return "\u21C5";
    } else {
      return "\u2191";
    }
  } else {
    if (down) {
      return "\u2193";
    } else {
      return "\u2713";
    }
  }
};

// src/util/testing.ts
import { exec } from "node:child_process";
import { mkdirSync } from "node:fs";
import { dirname, join } from "node:path";
import { promisify } from "node:util";
import pkgUp from "pkg-up";
import { asyncTimeout as asyncTimeout2 } from "@dxos/async";
import { failUndefined } from "@dxos/debug";
var BIN_PATH = join(dirname(pkgUp.sync({
  cwd: new URL(import.meta.url).pathname
}) ?? failUndefined()), "bin", "run");
var runCommand = async (command, cwd) => {
  mkdirSync(cwd, {
    recursive: true
  });
  const { stdout, stderr } = await asyncTimeout2(promisify(exec)(`${BIN_PATH} ${command}`, {
    cwd: __dirname
  }), 2e4);
  if (stderr) {
    throw new Error(stderr);
  }
  return stdout;
};

// src/base.ts
function _define_property(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }
  return obj;
}
var __dxlog_file3 = "/__w/dxos/dxos/packages/devtools/cli-base/src/base.ts";
var STDIN_TIMEOUT = 100;
var DEFAULT_CONFIG = "config/config-default.yml";
var exists = async (...args) => {
  try {
    const result = await stat(join2(...args));
    return !!result;
  } catch (err) {
    if (/ENOENT/.test(err.message)) {
      return false;
    } else {
      throw err;
    }
  }
};
var ARG_SPACE_KEYS = {
  key: Args.string({
    description: "Space key(s) head in hex."
  })
};
var FLAG_SPACE_KEYS = {
  key: Flags2.string({
    multiple: true,
    description: "Space key(s) head in hex."
  })
};
var AbstractBaseCommand = class extends Command {
  get clientConfig() {
    invariant3(this._clientConfig, void 0, {
      F: __dxlog_file3,
      L: 173,
      S: this,
      A: [
        "this._clientConfig",
        ""
      ]
    });
    return this._clientConfig;
  }
  get duration() {
    return Date.now() - this._startTime.getTime();
  }
  done() {
    this.log("ok");
  }
  /**
  * Load the client config.
  */
  async init() {
    await super.init();
    const { args, flags } = await this.parse({
      flags: this.ctor.flags,
      baseFlags: super.ctor.baseFlags,
      args: this.ctor.args,
      strict: this.ctor.strict
    });
    this.flags = flags;
    this.args = args;
    if (this.flags["json-log"]) {
      let pathOrFd;
      switch (this.flags["json-logfile"]) {
        case "-":
        case "stdout":
          pathOrFd = process.stdout.fd;
          break;
        case "stderr":
          pathOrFd = process.stderr.fd;
          break;
        default:
          pathOrFd = this.flags["json-logfile"];
      }
      log2.addProcessor(createFileProcessor({
        pathOrFd,
        // avoid collision with log's getConfig usage of LOG_FILTER for CONSOLE_LOGGER
        levels: process.env.JSON_LOG_FILTER ? [] : [
          LogLevel.ERROR,
          LogLevel.WARN,
          LogLevel.INFO
        ],
        filters: process.env.JSON_LOG_FILTER ? parseFilter(process.env.JSON_LOG_FILTER) : []
      }), void 0, {
        F: __dxlog_file3,
        L: 215,
        S: this,
        C: (f, a) => f(...a)
      });
      log2.config({
        filter: LogLevel.ERROR
      }, void 0, {
        F: __dxlog_file3,
        L: 224,
        S: this,
        C: (f, a) => f(...a)
      });
    } else {
      log2.config({
        filter: !process.env.LOG_FILTER && !process.env.LOG_CONFIG ? this.flags.verbose ? LogLevel.VERBOSE : LogLevel.ERROR : void 0
      }, void 0, {
        F: __dxlog_file3,
        L: 228,
        S: this,
        C: (f, a) => f(...a)
      });
      if (this.flags.verbose) {
        log2.verbose("verbose mode", void 0, {
          F: __dxlog_file3,
          L: 237,
          S: this,
          C: (f, a) => f(...a)
        });
      }
    }
    if (this.flags.target) {
      this.log("Using remote target:", this.flags.target);
      await this._loadConfigFromFile(Remote(this.flags.target, process.env.DX_AGENT_WS_AUTHENTICATION_TOKEN));
    } else {
      await this._loadConfigFromFile();
    }
    await this._initObservability(this.flags["json-log"] && this.flags["json-logfile"] === "stderr" ? (input) => process.stderr.write(JSON.stringify({
      input
    }) + "\n") : void 0);
  }
  async readStdin() {
    return new Promise((resolve) => {
      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
        terminal: process.stdin.isTTY
      });
      const inputLines = [];
      rl.on("line", (line) => inputLines.push(line));
      rl.on("close", () => resolve(inputLines.join("\n")));
      setTimeout(() => rl.close(), STDIN_TIMEOUT);
    });
  }
  async _initObservability(logCb = (input) => process.stderr.write(chalk3`{bold {magenta ${input} }}`)) {
    const observabilityState = await getObservabilityState(DX_DATA);
    const { mode, installationId, group } = observabilityState;
    if (mode === "disabled") {
      this.log("observability disabled by config");
      return;
    }
    if (group === "dxos") {
      this.log(chalk3`✨ {bgMagenta Running as internal user} ✨\n`);
    }
    await showObservabilityBanner(DX_DATA, (input) => {
      logCb(input);
    });
    let namespace = "cli";
    if (this.id === "agent:start") {
      namespace = "agent";
    }
    this._observability = await initializeNodeObservability({
      namespace,
      version: this.config.version,
      installationId,
      group,
      config: this._clientConfig,
      mode,
      tracingEnable: true,
      replayEnable: true
    });
    invariant3(this.id, void 0, {
      F: __dxlog_file3,
      L: 307,
      S: this,
      A: [
        "this.id",
        ""
      ]
    });
    this._observability.setTag("command", this.id, "telemetry");
  }
  /**
  * Load or create config file from defaults.
  * @private
  */
  async _loadConfigFromFile(...additionalConfig) {
    const { config: configFile } = this.flags;
    const configExists = await exists(configFile);
    if (!configExists) {
      const defaultConfigPath = join2(dirname2(pkgUp2.sync({
        cwd: import.meta.dirname
      }) ?? raise(new Error("Could not find package.json"))), DEFAULT_CONFIG);
      const yamlConfig = yaml.load(await readFile(defaultConfigPath, "utf-8"));
      {
        var _yamlConfig, _yamlConfig_runtime, _yamlConfig_runtime_client;
        (_yamlConfig = yamlConfig).runtime ?? (_yamlConfig.runtime = {});
        (_yamlConfig_runtime = yamlConfig.runtime).client ?? (_yamlConfig_runtime.client = {});
        (_yamlConfig_runtime_client = yamlConfig.runtime.client).storage ?? (_yamlConfig_runtime_client.storage = {});
        yamlConfig.runtime.client.storage.dataRoot = getProfilePath(yamlConfig.runtime.client.storage.dataRoot ?? DX_DATA, this.flags.profile);
      }
      await mkdir(dirname2(configFile), {
        recursive: true
      });
      await writeFile(configFile, yaml.dump(yamlConfig), "utf-8");
    }
    this._clientConfig = new Config(yaml.load(await readFile(configFile, "utf-8")), ...additionalConfig);
  }
  // TODO(burdon): Reconcile internal/external logging.
  /**
  * Use this method for user-facing messages.
  * Use @dxos/logger for internal messages.
  */
  log(message, ...args) {
    super.log(message, ...args);
  }
  /**
  * Use this method for user-facing warnings.
  * Use @dxos/logger for internal messages.
  */
  warn(err) {
    const message = typeof err === "string" ? err : err.message;
    super.logToStderr(chalk3`{red Warning}: ${message}`);
    return err;
  }
  catch(err, options) {
    this._observability?.captureException(err);
    this._failing = true;
    if (!this.flags || this.flags?.verbose) {
      super.error(err, options);
      return;
    }
    if (err instanceof FriendlyError) {
      this.logToStderr(chalk3`{red Error}: ${err.message}`);
      err.hint && this.logToStderr(chalk3`{gray HINT: ${err.hint}}`);
    } else {
      this.logToStderr(chalk3`{red Error}: Something went wrong. Use --verbose for more details.`);
    }
    this.exit(1);
  }
  /**
  * Called after each command run.
  */
  async finally() {
    const endTime = /* @__PURE__ */ new Date();
    const installationId = this._observability?.getTag("installationId");
    const did = this._observability?.getTag("did");
    if (this._observability) {
      this._observability?.track({
        installationId: installationId?.value,
        did: did?.value,
        action: "cli.command.run",
        properties: {
          status: this._failing ? "failure" : "success",
          duration: endTime.getTime() - this._startTime.getTime()
        }
      });
      await this._observability.close();
    }
    if (process.env.DX_TRACK_LEAKS) {
      global.dxDumpLeaks?.();
      globalThis.wtf.dump();
    }
    const stopFailsafe = setTimeout(() => {
      if (settings.debug) {
        this.log("timeout waiting for all promises to resolve, forcing exit");
      }
      process.exit(0);
    }, 1e3);
    stopFailsafe.unref();
  }
  async maybeStartDaemon() {
    if (this.flags.agent) {
      await this.execWithDaemon(async (daemon) => {
        const running = await daemon.isRunning(this.flags.profile);
        if (!running) {
          this.log(`Starting agent (${this.flags.profile})`);
          await daemon.start(this.flags.profile, {
            config: this.flags.config
          });
        }
      }, false);
    }
  }
  /**
  * Lazily create the client.
  */
  async getClient() {
    invariant3(this._clientConfig, void 0, {
      F: __dxlog_file3,
      L: 450,
      S: this,
      A: [
        "this._clientConfig",
        ""
      ]
    });
    if (!this._client) {
      try {
        if (this.flags.agent) {
          await this.maybeStartDaemon();
          this._client = new Client({
            config: this._clientConfig,
            services: fromAgent({
              profile: this.flags.profile
            })
          });
        } else {
          this._client = new Client({
            config: this._clientConfig
          });
        }
        await this._client.initialize();
      } catch (err) {
        if (err.message.includes("401")) {
          throw new ClientInitializationError("error authenticating with remote service", err);
        }
        throw new ClientInitializationError("error initializing client", err);
      }
    }
    return this._client;
  }
  /**
  * Get spaces and optionally wait until ready.
  */
  async getSpaces(client, { spaceKeys, includeHalo = true, wait = true } = {}) {
    await client.spaces.waitUntilReady();
    const spaces = client.spaces.get().filter((space) => (includeHalo || space !== client.spaces.default) && (!spaceKeys?.length || matchKeys(space.key, spaceKeys)));
    if (wait) {
      await Promise.all(spaces.map(async (space) => {
        if (space.state.get() === SpaceState2.SPACE_INITIALIZING) {
          await waitForSpace(space, this.flags.timeout, (err) => this.catch(err));
        }
      }));
    }
    return spaces;
  }
  /**
  * Get or select space.
  */
  async getSpace(client, key, wait = true) {
    await client.spaces.waitUntilReady();
    const spaces = await this.getSpaces(client, {
      wait
    });
    if (!spaces.length) {
      throw new Error("No spaces found.");
    }
    if (!key) {
      if (spaces.length === 1) {
        key = spaces[0].key.toHex();
      } else {
        key = await selectSpace(spaces);
      }
    }
    const space = spaces.find((space2) => space2.key.toHex().startsWith(key));
    if (!space) {
      this.catch(`Invalid key: ${key}`);
    } else {
      if (wait && space.state.get() === SpaceState2.SPACE_INITIALIZING) {
        await waitForSpace(space, this.flags.timeout, (err) => this.catch(err));
      }
      return space;
    }
  }
  async getSpaceById(client, id, { wait = true } = {}) {
    await client.spaces.waitUntilReady();
    const spaces = await this.getSpaces(client, {
      wait: false
    });
    const space = spaces.find((space2) => space2.id.startsWith(id));
    if (!space) {
      this.catch(`Invalid space: ${id}`);
    }
    if (wait) {
      await space.waitUntilReady();
    }
    return space;
  }
  /**
  * Execute callback with the given space(s).
  */
  // TODO(burdon): Convert most commands to work with this.
  async execWithSpace(callback, options = {}) {
    const client = await this.getClient();
    await this.onClientInit(client);
    const spaces = options.spaceKeys?.length || options.all ? await this.getSpaces(client, {
      spaceKeys: options.spaceKeys
    }) : [
      await this.getSpace(client)
    ];
    const values = [];
    for (const space of spaces) {
      if (options.verbose) {
        this.log(`Space: ${space.key.truncate()}`);
      }
      if (options.types) {
        for (const type of options.types) {
          if (!space.db.graph.schemaRegistry.hasSchema(type)) {
            space.db.graph.schemaRegistry.addSchema([
              type
            ]);
          }
        }
      }
      const value = await callback({
        client,
        space
      });
      await space.db.flush();
      if (value) {
        values.push(value);
      }
    }
    await client.destroy();
    return values;
  }
  /**
  * Convenience function to wrap command passing in client object.
  */
  async execWithClient(callback, options = {
    halo: true
  }) {
    const client = await this.getClient();
    if (options.halo) {
      if (!client.halo.identity.get()) {
        this.warn("Identity not initialized.");
        process.exit(1);
      }
      await new Promise((resolve) => {
        if (client.halo.devices.get().length > 0) {
          resolve(void 0);
        } else {
          const subscription = client.halo.devices.subscribe((devices) => {
            if (devices.length > 0) {
              subscription.unsubscribe();
              resolve(void 0);
            }
          });
        }
      });
      client.edge.setIdentity(createEdgeIdentity(client));
    }
    await this.onClientInit(client);
    const value = await callback({
      client
    });
    await client.destroy();
    return value;
  }
  /**
  * Hook for client initialization.
  */
  async onClientInit(client) {
  }
  /**
  * Convenience function to wrap starting the agent.
  */
  async execWithDaemon(callback, system) {
    const platform = os.platform();
    const daemon = system ? new SystemDaemon(DX_RUNTIME, platform === "darwin" ? new LaunchctlRunner() : platform === "linux" ? new SystemctlRunner() : raise(new Error(`System daemon not implemented for ${os.platform()}.`))) : new PhoenixDaemon(DX_RUNTIME);
    await daemon.connect();
    const value = await callback(daemon);
    await daemon.disconnect();
    return value;
  }
  parseJson(filename) {
    try {
      return JSON.parse(String(fs.readFileSync(filename)));
    } catch (err) {
      log2.error("error parsing file", {
        filename,
        err
      }, {
        F: __dxlog_file3,
        L: 671,
        S: this,
        C: (f, a) => f(...a)
      });
      throw err;
    }
  }
  constructor(argv, config) {
    super(argv, config), _define_property(this, "_clientConfig", void 0), _define_property(this, "_client", void 0), _define_property(this, "_failing", false), _define_property(this, "_startTime", void 0), _define_property(this, "_observability", void 0), _define_property(this, "flags", void 0), _define_property(this, "args", void 0);
    this._startTime = /* @__PURE__ */ new Date();
  }
};
_define_property(AbstractBaseCommand, "enableJsonFlag", true);
_define_property(AbstractBaseCommand, "flags", {
  // TODO(burdon): Check fixed in v4.
  // Even though oclif should support this out of the box there seems to be a bug.
  json: Flags2.boolean({
    description: "Output as JSON.",
    default: false
  }),
  verbose: Flags2.boolean({
    char: "v",
    description: "Verbose output",
    default: false
  }),
  "dry-run": Flags2.boolean({
    description: "Dry run.",
    default: false
  }),
  profile: Flags2.string({
    description: "User profile.",
    default: ENV_DX_PROFILE_DEFAULT,
    env: ENV_DX_PROFILE
  }),
  config: Flags2.string({
    env: ENV_DX_CONFIG,
    description: "Config file.",
    helpValue: "path",
    async default({ flags }) {
      const profile = flags?.profile ?? ENV_DX_PROFILE_DEFAULT;
      return getProfilePath(DX_CONFIG, profile) + ".yml";
    },
    dependsOn: [
      "profile"
    ],
    aliases: [
      "c"
    ]
  }),
  timeout: Flags2.integer({
    description: "Timeout (ms).",
    default: 6e4,
    aliases: [
      "t"
    ]
  }),
  target: Flags2.string({
    description: "Target websocket server."
  }),
  agent: Flags2.boolean({
    description: "Run command with agent.",
    default: true,
    allowNo: true,
    env: ENV_DX_NO_AGENT
  }),
  // For consumption by Docker/Kubernetes/other log collection agents, write JSON logs to stderr.
  // Use stdout for user-facing interaction, and potentially JSON formatted output.
  // TODO(burdon): unify output/logging with oclif.
  "json-log": Flags2.boolean({
    description: "When running in foreground, log JSON format"
  }),
  "json-logfile": Flags2.string({
    description: "JSON log file destination, or 'stdout' or 'stderr'",
    default: "stderr"
  })
});
export {
  ARG_SPACE_KEYS,
  AbstractBaseCommand,
  AgentAlreadyRunningError,
  BIN_PATH,
  ClientInitializationError,
  FLAG_SPACE_KEYS,
  FriendlyError,
  IDENTITY_WAIT_TIMEOUT,
  IdentityWaitTimeoutError,
  SPACE_WAIT_TIMEOUT,
  SpaceTimeoutError,
  TABLE_FLAGS,
  acceptInvitation,
  createIpfsClient,
  hostInvitation,
  importESM,
  mapCredentials,
  mapDevices,
  mapMembers,
  mapSpaces,
  matchKeys,
  maybeTruncateKey,
  printAgents,
  printCredentials,
  printDevices,
  printMembers,
  printSpaces,
  runCommand,
  selectSpace,
  stringify,
  table,
  waitForSpace
};
//# sourceMappingURL=index.mjs.map
