@aident-ai/cli 0.3.0 → 0.3.1-rc.0

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 (3) hide show
  1. package/dist/cli.mjs +314 -82
  2. package/dist/dsh.mjs +2970 -47
  3. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -3354,7 +3354,7 @@ import { mkdir as mkdir2 } from "node:fs/promises";
3354
3354
  import { dirname as dirname2, posix, resolve as resolve2, sep } from "node:path";
3355
3355
  import { Readable, Transform } from "node:stream";
3356
3356
  import { pipeline } from "node:stream/promises";
3357
- import { createGunzip } from "node:zlib";
3357
+ import { createGunzip, gzipSync } from "node:zlib";
3358
3358
 
3359
3359
  // ../../node_modules/tar-stream/index.js
3360
3360
  var $extract = require_extract();
@@ -7191,11 +7191,13 @@ var AgentSkillBundleVersionSchema = stringType().regex(/^[0-9]+\.[0-9]+\.[0-9]+(
7191
7191
  var AgentSkillBundleSha256Schema = stringType().regex(/^[a-f0-9]{64}$/);
7192
7192
  var AgentSkillBundleGitShaSchema = stringType().regex(/^[a-f0-9]{40}$/);
7193
7193
  var AgentSkillBundleRightsBasisSchema = nativeEnumType(AgentSkillBundleRightsBasis);
7194
+ var AgentSkillBundleSourcePathSchema = stringType().min(1).max(300).regex(/^[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)*$/).refine((value) => value.split("/").every((segment) => segment !== "." && segment !== ".."));
7194
7195
  var AgentSkillBundlePublishManifestSchema = objectType({
7195
7196
  name: AgentSkillBundleNameSchema,
7196
7197
  version: AgentSkillBundleVersionSchema,
7197
7198
  sourceOwner: stringType().regex(/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/).max(39).refine((value) => !value.includes("--")),
7198
7199
  sourceRepository: stringType().regex(/^[A-Za-z0-9_.][A-Za-z0-9_.-]{0,99}$/).refine((value) => value !== "." && value !== ".."),
7200
+ sourcePath: AgentSkillBundleSourcePathSchema.optional(),
7199
7201
  sourceCommit: AgentSkillBundleGitShaSchema,
7200
7202
  sourceTree: AgentSkillBundleGitShaSchema,
7201
7203
  expectedSha256: AgentSkillBundleSha256Schema,
@@ -7387,25 +7389,9 @@ async function installAgentSkillBundle(descriptorValue, options = {}, injected =
7387
7389
  const descriptor = AgentSkillBundleInstallDescriptorSchema.parse(descriptorValue);
7388
7390
  const home = options.home ?? homedir2();
7389
7391
  const env = options.env ?? process.env;
7390
- const stateRoot = join3(home, ".aident", "skills", "internal", descriptor.name);
7391
- const releasesRoot = join3(stateRoot, "releases");
7392
- const releaseDirectory = join3(releasesRoot, descriptor.artifactSha256);
7393
- await mkdir3(stateRoot, { recursive: true, mode: 448 });
7394
- const lock = await tryAcquireDirectoryLock(join3(stateRoot, "install.lock"));
7395
- if (!lock)
7396
- throw new Error(`Another ${descriptor.name} installation is already running`);
7397
- try {
7398
- if (!await pathExists(releaseDirectory)) {
7399
- const archive = await downloadArchive(descriptor, injected.fetch ?? fetch);
7400
- await materializeRelease(archive, descriptor, releasesRoot, releaseDirectory);
7401
- } else {
7402
- const existingRelease = await lstat2(releaseDirectory);
7403
- if (!existingRelease.isDirectory()) {
7404
- throw new Error(`Existing agent Skill release is not a directory: ${releaseDirectory}`);
7405
- }
7406
- await assertInstalledSkillIdentity(releaseDirectory, descriptor.name);
7407
- }
7408
- const targets = await reconcileTargets(descriptor.name, releaseDirectory, releasesRoot, home, env);
7392
+ return withInstallLock(descriptor.name, home, async () => {
7393
+ const { releaseDirectory } = await prepareRelease(descriptor, home, injected.fetch ?? fetch);
7394
+ const targets = await reconcileTargets(descriptor.name, releaseDirectory, dirname3(releaseDirectory), home, env);
7409
7395
  return {
7410
7396
  name: descriptor.name,
7411
7397
  version: descriptor.version,
@@ -7414,6 +7400,37 @@ async function installAgentSkillBundle(descriptorValue, options = {}, injected =
7414
7400
  success: targets.every((target) => target.status !== "conflict" /* Conflict */ && target.status !== "failed" /* Failed */),
7415
7401
  targets
7416
7402
  };
7403
+ });
7404
+ }
7405
+ async function prepareAgentSkillBundleRelease(descriptorValue, options = {}, injected = {}) {
7406
+ const descriptor = AgentSkillBundleInstallDescriptorSchema.parse(descriptorValue);
7407
+ const home = options.home ?? homedir2();
7408
+ return withInstallLock(descriptor.name, home, () => prepareRelease(descriptor, home, injected.fetch ?? fetch));
7409
+ }
7410
+ async function prepareRelease(descriptor, home, fetchImpl) {
7411
+ const stateRoot = join3(home, ".aident", "skills", "internal", descriptor.name);
7412
+ const releasesRoot = join3(stateRoot, "releases");
7413
+ const releaseDirectory = join3(releasesRoot, descriptor.artifactSha256);
7414
+ if (!await pathExists(releaseDirectory)) {
7415
+ const archive = await downloadArchive(descriptor, fetchImpl);
7416
+ await materializeRelease(archive, descriptor, releasesRoot, releaseDirectory);
7417
+ } else {
7418
+ const existingRelease = await lstat2(releaseDirectory);
7419
+ if (!existingRelease.isDirectory()) {
7420
+ throw new Error(`Existing agent Skill release is not a directory: ${releaseDirectory}`);
7421
+ }
7422
+ await assertInstalledSkillIdentity(releaseDirectory, descriptor.name);
7423
+ }
7424
+ return { descriptor, releaseDirectory };
7425
+ }
7426
+ async function withInstallLock(name, home, run) {
7427
+ const stateRoot = join3(home, ".aident", "skills", "internal", name);
7428
+ await mkdir3(stateRoot, { recursive: true, mode: 448 });
7429
+ const lock = await tryAcquireDirectoryLock(join3(stateRoot, "install.lock"));
7430
+ if (!lock)
7431
+ throw new Error(`Another ${name} installation is already running`);
7432
+ try {
7433
+ return await run();
7417
7434
  } finally {
7418
7435
  await lock.release();
7419
7436
  }
@@ -7689,7 +7706,7 @@ function readLine(prompt, options = {}) {
7689
7706
  var CLIENT_NAME = "aident-cli";
7690
7707
  var LOOPBACK_HOST = "127.0.0.1";
7691
7708
  var LOGIN_OPEN_TIMEOUT_MS = 90 * 1000;
7692
- var LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS = 5 * 60 * 1000;
7709
+ var LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS = 30 * 60 * 1000;
7693
7710
  var LOGIN_OPENED_MESSAGE = `User opened the Aident login window. They have ${LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS / 60000} minutes to finish; if it fails, run \`aident login --oob\`, share the URL, and ask for the verification code.`;
7694
7711
  async function login(options) {
7695
7712
  const baseUrl = options.baseUrl.replace(/\/+$/, "");
@@ -8167,7 +8184,7 @@ function getConfigFile() {
8167
8184
  function getConfigFileForHome(home) {
8168
8185
  return join4(getAidentDir(home), "config.json");
8169
8186
  }
8170
- var DEFAULT_BASE_URL = "https://loadout.aident.ai"?.trim() || "https://loadout.aident.ai";
8187
+ var DEFAULT_BASE_URL = "https://rc.loadout.aident.ai"?.trim() || "https://loadout.aident.ai";
8171
8188
  var CONFIG_KEYS = ["baseUrl", "packages"];
8172
8189
  async function readConfig(configFile = getConfigFile()) {
8173
8190
  try {
@@ -8460,7 +8477,7 @@ function serializeLoadoutSkillPackage(skillPackage) {
8460
8477
  }
8461
8478
 
8462
8479
  // src/version.ts
8463
- var VERSION = "0.3.0";
8480
+ var VERSION = "0.3.1-rc.0";
8464
8481
  var DESKTOP_VERSION = process.env.AIDENT_DESKTOP_VERSION?.trim() || undefined;
8465
8482
  var DESKTOP_CHANNEL = parseDesktopChannel(process.env.AIDENT_DESKTOP_CHANNEL);
8466
8483
  function parseDesktopChannel(value) {
@@ -9353,7 +9370,7 @@ var LoadoutFavoriteSkillsUpdateSchema = objectType({
9353
9370
  syncRequired: booleanType()
9354
9371
  }).strict();
9355
9372
  function getLoadoutFavoriteSkillsStateToken(skills) {
9356
- const state = skills.map(({ name, artifactVersionId }) => ({ name, artifactVersionId })).sort((left, right) => left.name.localeCompare(right.name));
9373
+ const state = skills.map(({ name, artifactVersionId, deliveryRevision }) => ({ name, artifactVersionId, deliveryRevision })).sort((left, right) => left.name.localeCompare(right.name));
9357
9374
  return createHash3("sha256").update(JSON.stringify(state)).digest("hex");
9358
9375
  }
9359
9376
 
@@ -9517,10 +9534,11 @@ var KNOWN_CLEAN_LOADOUT_SKILL_REQUIRED_FILE_GIT_BLOB_SHA1 = {
9517
9534
  }
9518
9535
  ]
9519
9536
  };
9520
- async function fetchLoadoutSkillMetadata(baseUrl) {
9537
+ async function fetchLoadoutSkillMetadata(baseUrl, signal) {
9521
9538
  const metadataUrl = `${baseUrl.replace(/\/+$/, "")}/.well-known/loadout-skill.json`;
9522
9539
  const res = await fetch(metadataUrl, {
9523
9540
  method: "GET",
9541
+ signal,
9524
9542
  headers: {
9525
9543
  "User-Agent": `@aident-ai/cli/${VERSION}`,
9526
9544
  [AIDENT_UPDATE_PROTOCOL_HEADER]: AIDENT_UPDATE_PROTOCOL_VERSION
@@ -10090,7 +10108,7 @@ function isExpired(creds) {
10090
10108
 
10091
10109
  // src/loadoutFavoriteSkills.ts
10092
10110
  import { createHash as createHash5 } from "node:crypto";
10093
- import { lstat as lstat5, mkdir as mkdir8, readFile as readFile8, readlink, rename as rename4, rm as rm3, symlink as symlink3, writeFile as writeFile6 } from "node:fs/promises";
10111
+ import { cp, lstat as lstat5, mkdir as mkdir8, readFile as readFile8, readlink, rename as rename4, rm as rm3, symlink as symlink3, writeFile as writeFile6 } from "node:fs/promises";
10094
10112
  import { homedir as homedir5 } from "node:os";
10095
10113
  import path from "node:path";
10096
10114
 
@@ -10169,6 +10187,8 @@ function assertPackage(pkg) {
10169
10187
  }
10170
10188
  if (packageBytes > LOADOUT_SKILL_MAX_PACKAGE_BYTES)
10171
10189
  throw new Error(`Package is too large: ${pkg.name}`);
10190
+ if (pkg.bundle)
10191
+ AgentSkillBundleInstallDescriptorSchema.parse(pkg.bundle);
10172
10192
  }
10173
10193
  function quoteYaml(value) {
10174
10194
  return JSON.stringify(value);
@@ -10211,7 +10231,7 @@ async function readManifest(home, env) {
10211
10231
  throw new Error("Unsupported manifest");
10212
10232
  const harnessRoots = new Set(getHarnessHosts(home, env).map(({ root }) => path.join(root, "skills")));
10213
10233
  for (const skill of value.skills) {
10214
- if (!LOADOUT_SKILL_NAME_PATTERN.test(skill.name) || !isCanonicalUuid(skill.artifactVersionId) || skill.displayName !== undefined && typeof skill.displayName !== "string" || skill.summary !== undefined && typeof skill.summary !== "string" || !/^loadout-[a-z0-9-]+-[0-9a-f]{8}$/.test(skill.commandName) || skill.installPath !== path.join(getCanonicalRoot(home, env), skill.commandName) || skill.linkPaths.some((linkPath) => !harnessRoots.has(path.dirname(linkPath)) || path.basename(linkPath) !== skill.commandName)) {
10234
+ if (!LOADOUT_SKILL_NAME_PATTERN.test(skill.name) || !isCanonicalUuid(skill.artifactVersionId) || skill.displayName !== undefined && typeof skill.displayName !== "string" || skill.summary !== undefined && typeof skill.summary !== "string" || skill.deliveryRevision !== undefined && !/^[a-f0-9]{64}$/.test(skill.deliveryRevision) || !/^loadout-[a-z0-9-]+-[0-9a-f]{8}$/.test(skill.commandName) || skill.installPath !== path.join(getCanonicalRoot(home, env), skill.commandName) || skill.linkPaths.some((linkPath) => !harnessRoots.has(path.dirname(linkPath)) || path.basename(linkPath) !== skill.commandName)) {
10215
10235
  throw new Error("Invalid favorite Skills ownership manifest");
10216
10236
  }
10217
10237
  }
@@ -10245,7 +10265,11 @@ async function removeOwnedSkill(skill) {
10245
10265
  if (await pathExists3(skill.installPath))
10246
10266
  await rm3(skill.installPath, { recursive: true });
10247
10267
  }
10248
- async function reconcileLoadoutFavoriteSkills(payload, home = getHome(), env = process.env) {
10268
+ async function writeBundlePackage(directory, pkg, releaseDirectory) {
10269
+ await cp(releaseDirectory, directory, { recursive: true });
10270
+ await writeFile6(path.join(directory, LOADOUT_SKILL_ENTRYPOINT), entrypointContent(pkg, await readFile8(path.join(releaseDirectory, LOADOUT_SKILL_ENTRYPOINT), "utf8")));
10271
+ }
10272
+ async function reconcileLoadoutFavoriteSkills(payload, home = getHome(), env = process.env, injected = {}) {
10249
10273
  const manifest = await readManifest(home, env);
10250
10274
  const previousByName = new Map(manifest.skills.map((skill) => [skill.name, skill]));
10251
10275
  const desiredNames = new Set(payload.packages.map((pkg) => pkg.name));
@@ -10260,6 +10284,12 @@ async function reconcileLoadoutFavoriteSkills(payload, home = getHome(), env = p
10260
10284
  if (payload.unavailable.some((name) => desiredNames.has(name))) {
10261
10285
  throw new Error("A favorite Skill cannot be both available and unavailable");
10262
10286
  }
10287
+ const preparedBundles = new Map;
10288
+ for (const pkg of payload.packages) {
10289
+ if (!pkg.bundle)
10290
+ continue;
10291
+ preparedBundles.set(pkg.name, await (injected.prepareBundleRelease ?? prepareAgentSkillBundleRelease)(pkg.bundle, { home, env }));
10292
+ }
10263
10293
  const nextSkills = [];
10264
10294
  const result = {
10265
10295
  installed: [],
@@ -10301,21 +10331,26 @@ async function reconcileLoadoutFavoriteSkills(payload, home = getHome(), env = p
10301
10331
  continue;
10302
10332
  }
10303
10333
  const allLinksExist = (await Promise.all(linkPaths.map(pathExists3))).every(Boolean);
10304
- if (previous?.artifactVersionId === pkg.artifactVersionId && previous.displayName === pkg.displayName && previous.summary === pkg.summary && previous.installPath === installPath && previous.linkPaths.length === linkPaths.length && previous.linkPaths.every((linkPath) => linkPaths.includes(linkPath)) && await pathExists3(installPath) && allLinksExist) {
10334
+ if (previous?.artifactVersionId === pkg.artifactVersionId && previous.displayName === pkg.displayName && previous.summary === pkg.summary && previous.deliveryRevision === pkg.bundle?.artifactSha256 && previous.installPath === installPath && previous.linkPaths.length === linkPaths.length && previous.linkPaths.every((linkPath) => linkPaths.includes(linkPath)) && await pathExists3(installPath) && allLinksExist) {
10305
10335
  result.unchanged.push(pkg.name);
10306
10336
  nextSkills.push(previous);
10307
10337
  continue;
10308
10338
  }
10339
+ const preparedBundle = preparedBundles.get(pkg.name);
10309
10340
  const temporaryPath = `${installPath}.${process.pid}.tmp`;
10310
10341
  const backupPath = `${installPath}.${process.pid}.backup`;
10311
10342
  await mkdir8(getCanonicalRoot(home, env), { recursive: true });
10312
10343
  await rm3(temporaryPath, { recursive: true, force: true });
10313
10344
  await rm3(backupPath, { recursive: true, force: true });
10314
- await mkdir8(temporaryPath, { recursive: true });
10315
- for (const file of pkg.files) {
10316
- const destination = path.join(temporaryPath, ...file.path.split("/"));
10317
- await mkdir8(path.dirname(destination), { recursive: true });
10318
- await writeFile6(destination, file.path === LOADOUT_SKILL_ENTRYPOINT ? entrypointContent(pkg, file.content) : file.content);
10345
+ if (preparedBundle) {
10346
+ await writeBundlePackage(temporaryPath, pkg, preparedBundle.releaseDirectory);
10347
+ } else {
10348
+ await mkdir8(temporaryPath, { recursive: true });
10349
+ for (const file of pkg.files) {
10350
+ const destination = path.join(temporaryPath, ...file.path.split("/"));
10351
+ await mkdir8(path.dirname(destination), { recursive: true });
10352
+ await writeFile6(destination, file.path === LOADOUT_SKILL_ENTRYPOINT ? entrypointContent(pkg, file.content) : file.content);
10353
+ }
10319
10354
  }
10320
10355
  const createdLinkPaths = [];
10321
10356
  let backedUp = false;
@@ -10350,7 +10385,8 @@ async function reconcileLoadoutFavoriteSkills(payload, home = getHome(), env = p
10350
10385
  summary: pkg.summary,
10351
10386
  commandName: nextCommandName,
10352
10387
  installPath,
10353
- linkPaths
10388
+ linkPaths,
10389
+ ...pkg.bundle ? { deliveryRevision: pkg.bundle.artifactSha256 } : {}
10354
10390
  });
10355
10391
  (previous ? result.updated : result.installed).push(pkg.name);
10356
10392
  }
@@ -11357,7 +11393,7 @@ async function readBoundedResponseBody(response, expectedByteLength, maximumByte
11357
11393
 
11358
11394
  // src/minisign.ts
11359
11395
  import { createHash as createHash7, createPublicKey, verify } from "node:crypto";
11360
- var ACTIVE_PUBLIC_KEYS = JSON.parse('{"production-1":"RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3"}');
11396
+ var ACTIVE_PUBLIC_KEYS = JSON.parse('{"aident-desktop-release-2026-08-v2":"RWSuGigRGuwWblBdVlTya0xk4E3OlM2YOWrxndkGuO5ZN3tq5JdpF45a"}');
11361
11397
  var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
11362
11398
  function verifyMinisign(bytes, signatureText, signingKeyId) {
11363
11399
  const encodedKey = ACTIVE_PUBLIC_KEYS[signingKeyId];
@@ -11395,7 +11431,7 @@ import { chmod, lstat as lstat6, mkdir as mkdir9, open, readFile as readFile10,
11395
11431
  import { dirname as dirname8, isAbsolute as isAbsolute3, relative as relative2, resolve as resolve4 } from "node:path";
11396
11432
  import { promisify } from "node:util";
11397
11433
  var execFile2 = promisify(execFileCallback);
11398
- var windowsAccount;
11434
+ var windowsAccountSid;
11399
11435
  async function readFileIfPresent(path) {
11400
11436
  try {
11401
11437
  const entry = await lstat6(path);
@@ -11471,17 +11507,23 @@ function hasCode3(error, code) {
11471
11507
  async function protectWindowsPath(path, directory) {
11472
11508
  if (process.platform !== "win32")
11473
11509
  return;
11474
- const account = await getWindowsAccount();
11475
- await execFile2("icacls.exe", [path, "/inheritance:r", "/grant:r", `${account}:${directory ? "(OI)(CI)" : ""}F`]);
11510
+ const sid = await getWindowsAccountSid();
11511
+ await execFile2("icacls.exe", [path, "/inheritance:r", "/grant:r", `*${sid}:${directory ? "(OI)(CI)" : ""}F`]);
11476
11512
  }
11477
- async function getWindowsAccount() {
11478
- windowsAccount ??= execFile2("whoami.exe").then(({ stdout }) => stdout.trim());
11479
- const pending = windowsAccount;
11513
+ function parseWindowsAccountSid(output) {
11514
+ const match = output.toString("latin1").match(/,"(S-1-\d+(?:-\d+)+)"\s*$/);
11515
+ if (!match)
11516
+ throw new Error("Windows account SID is unavailable");
11517
+ return match[1];
11518
+ }
11519
+ async function getWindowsAccountSid() {
11520
+ windowsAccountSid ??= execFile2("whoami.exe", ["/user", "/fo", "csv", "/nh"], { encoding: "buffer" }).then(({ stdout }) => parseWindowsAccountSid(stdout));
11521
+ const pending = windowsAccountSid;
11480
11522
  try {
11481
11523
  return await pending;
11482
11524
  } catch (error) {
11483
- if (windowsAccount === pending)
11484
- windowsAccount = undefined;
11525
+ if (windowsAccountSid === pending)
11526
+ windowsAccountSid = undefined;
11485
11527
  throw error;
11486
11528
  }
11487
11529
  }
@@ -11803,22 +11845,51 @@ import { execFile as execFileCallback2 } from "node:child_process";
11803
11845
  import { constants as constants3 } from "node:fs";
11804
11846
  import { access as access2, realpath as realpath5 } from "node:fs/promises";
11805
11847
  import { homedir as homedir8 } from "node:os";
11806
- import { basename as basename3, delimiter as delimiter2, isAbsolute as isAbsolute5, join as join11 } from "node:path";
11848
+ import { basename as basename3, delimiter as delimiter2, isAbsolute as isAbsolute5, join as join11, win32 as win323 } from "node:path";
11807
11849
  import { promisify as promisify2 } from "node:util";
11808
11850
  var execFile3 = promisify2(execFileCallback2);
11809
11851
  var SUPPORTED_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
11852
+ var WINDOWS_ENV_KEYS = new Map([
11853
+ ["path", "PATH"],
11854
+ ["systemroot", "SystemRoot"],
11855
+ ["comspec", "ComSpec"]
11856
+ ]);
11857
+ var PROBE_TIMEOUT_DETAIL = "Windows CLI verification timed out; retry after the machine finishes starting up";
11858
+ var WINDOWS_PROBE_CLEANUP_TIMEOUT_MS = 2000;
11810
11859
  async function inspectAidentCliEntrypoint(home = homedir8(), env = process.env, platform = process.platform, injected = {}) {
11860
+ if (platform === "win32") {
11861
+ env = Object.fromEntries(Object.entries(env).map(([key, value]) => [WINDOWS_ENV_KEYS.get(key.toLowerCase()) ?? key, value]));
11862
+ }
11811
11863
  const resolveNativePath = injected.resolveNativePath ?? (async (homeDirectory) => (await readActiveNativeInstallation(homeDirectory, getActiveCliExecutablePath(env)))?.receipt.cliPath ?? null);
11812
- const [active, nativePath] = await Promise.all([
11813
- (injected.resolveActiveCommand ?? resolveActiveAidentCommand)(env, platform),
11864
+ const probeTimeoutMs = platform === "win32" ? injected.probeTimeoutMs ?? 1e4 : undefined;
11865
+ if (probeTimeoutMs !== undefined && probeTimeoutMs <= 0)
11866
+ throw new RangeError("Windows probe timeout must be positive");
11867
+ const commandDeadline = probeTimeoutMs ? AbortSignal.timeout(probeTimeoutMs * 2 - Math.min(probeTimeoutMs, WINDOWS_PROBE_CLEANUP_TIMEOUT_MS)) : undefined;
11868
+ const runProbe = injected.execFile ?? ((file, args, env, platform) => runFile(file, args, env, platform, commandDeadline, probeTimeoutMs, injected.spawnProbe));
11869
+ const probe = async (operation) => {
11870
+ let timedOut = false;
11871
+ const executeFile = async (file, args, env, platform) => {
11872
+ try {
11873
+ return await runProbe(file, args, env, platform);
11874
+ } catch (error) {
11875
+ if (platform === "win32" && error instanceof Error && error.name === "TimeoutError")
11876
+ timedOut = true;
11877
+ throw error;
11878
+ }
11879
+ };
11880
+ return { value: await operation(executeFile), timedOut };
11881
+ };
11882
+ const [resolution, nativePath] = await Promise.all([
11883
+ probe((executeFile) => injected.resolveActiveCommand ? injected.resolveActiveCommand(env, platform) : resolveActiveAidentCommand(env, platform, executeFile)),
11814
11884
  resolveNativePath(home)
11815
11885
  ]);
11886
+ const active = resolution.value;
11816
11887
  if (!active) {
11817
11888
  return {
11818
11889
  ok: false,
11819
- owner: "missing",
11890
+ owner: resolution.timedOut ? "unknown" /* Unknown */ : "missing",
11820
11891
  ...nativePath ? { nativePath } : {},
11821
- detail: nativePath ? "The bundled Aident CLI is installed but the aident command is not available on PATH" : "The aident command is not available on PATH"
11892
+ detail: resolution.timedOut ? PROBE_TIMEOUT_DETAIL : nativePath ? "The bundled Aident CLI is installed but the aident command is not available on PATH" : "The aident command is not available on PATH"
11822
11893
  };
11823
11894
  }
11824
11895
  const resolveRealpath = injected.realpath ?? realpath5;
@@ -11826,7 +11897,7 @@ async function inspectAidentCliEntrypoint(home = homedir8(), env = process.env,
11826
11897
  resolveRealpath(active.path).catch(() => active.path),
11827
11898
  nativePath ? resolveRealpath(nativePath).catch(() => nativePath) : null
11828
11899
  ]);
11829
- const version = await (injected.readVersion ?? readAidentCliVersion)(active.path, active.env);
11900
+ const { value: version, timedOut: versionTimedOut } = await probe((executeFile) => injected.readVersion ? injected.readVersion(active.path, active.env) : readAidentCliVersion(active.path, active.env, platform, executeFile));
11830
11901
  const requiredVersion = injected.requiredVersion ?? VERSION;
11831
11902
  const current = version !== null && isAidentSemver(version) && compareAidentSemver(version, requiredVersion) >= 0;
11832
11903
  if (nativeRealpath && activeRealpath === nativeRealpath) {
@@ -11836,42 +11907,70 @@ async function inspectAidentCliEntrypoint(home = homedir8(), env = process.env,
11836
11907
  ...version ? { version } : {},
11837
11908
  activePath: active.path,
11838
11909
  nativePath,
11839
- detail: version ? current ? "The native Aident CLI is the active command" : `The active Aident CLI v${version} must be updated to v${requiredVersion} or newer before onboarding` : "The native Aident CLI could not report its version"
11910
+ detail: version ? current ? "The native Aident CLI is the active command" : `The active Aident CLI v${version} must be updated to v${requiredVersion} or newer before onboarding` : versionTimedOut ? PROBE_TIMEOUT_DETAIL : "The native Aident CLI could not report its version"
11840
11911
  };
11841
11912
  }
11842
11913
  const detectOwnership = injected.detectOwnership ?? detectCliInstallOwnership;
11843
- const ownership = await detectOwnership(active.path, home, {
11914
+ const { value: ownership, timedOut: ownershipTimedOut } = await probe((executeFile) => detectOwnership(active.path, home, {
11844
11915
  platform,
11845
- execFile: (file, args) => runFile(file, args, active.env),
11916
+ execFile: (file, args) => executeFile(file, args, active.env, platform),
11846
11917
  resolveExecutable: (file) => resolveExecutable(file, active.env, platform)
11847
- });
11918
+ }));
11848
11919
  const packageManaged = [
11849
11920
  "npm" /* Npm */,
11850
11921
  "pnpm" /* Pnpm */,
11851
11922
  "bun" /* Bun */,
11852
11923
  "volta" /* Volta */
11853
11924
  ].includes(ownership.method);
11925
+ let detail = "The active Aident CLI owner could not be verified";
11926
+ if (ownership.method === "ephemeral" /* Ephemeral */) {
11927
+ detail = "An ephemeral Aident CLI cannot be used as the persistent global entrypoint";
11928
+ } else if (packageManaged) {
11929
+ if (nativePath) {
11930
+ detail = `The ${ownership.method}-managed Aident CLI is shadowing the installed native command; reinstall Aident to replace it`;
11931
+ } else if (current) {
11932
+ detail = `The Aident CLI is managed by ${ownership.method}`;
11933
+ } else {
11934
+ detail = `Run aident update --cli-only to update the active ${ownership.method}-managed CLI to v${requiredVersion} or newer`;
11935
+ }
11936
+ }
11937
+ if (ownership.method === "unknown" /* Unknown */ && ownershipTimedOut || versionTimedOut && !nativePath && ownership.method !== "ephemeral" /* Ephemeral */) {
11938
+ detail = PROBE_TIMEOUT_DETAIL;
11939
+ }
11854
11940
  return {
11855
11941
  ok: packageManaged && current && !nativePath,
11856
11942
  owner: ownership.method,
11857
11943
  ...version ? { version } : {},
11858
11944
  activePath: active.path,
11859
11945
  ...nativePath ? { nativePath } : {},
11860
- detail: packageManaged ? nativePath ? `The ${ownership.method}-managed Aident CLI is shadowing the installed native command; reinstall Aident to replace it` : current ? `The Aident CLI is managed by ${ownership.method}` : `Run aident update --cli-only to update the active ${ownership.method}-managed CLI to v${requiredVersion} or newer` : ownership.method === "ephemeral" /* Ephemeral */ ? "An ephemeral Aident CLI cannot be used as the persistent global entrypoint" : "The active Aident CLI owner could not be verified"
11946
+ detail
11861
11947
  };
11862
11948
  }
11863
- async function resolveActiveAidentCommand(env, platform) {
11949
+ async function resolveActiveAidentCommand(env, platform, executeFile) {
11864
11950
  if (platform === "win32") {
11865
- const output = await execFile3("where.exe", ["aident"], { env, windowsHide: true }).catch(() => null);
11866
- const path = output?.stdout.split(/\r?\n/).find((line) => isAbsolute5(line.trim()))?.trim();
11867
- return path ? { path, env } : null;
11951
+ const command = "$ErrorActionPreference = 'Stop'; $env:Path = ([Environment]::GetEnvironmentVariable('Path', 'Machine'), [Environment]::GetEnvironmentVariable('Path', 'User') | Where-Object { $_ }) -join ';'; $active = Get-Command aident -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1; $json = @{ path = $active.Source; pathValue = $env:Path } | ConvertTo-Json -Compress; [regex]::Replace($json, '[^\\x00-\\x7F]', { param($match) '\\u{0:x4}' -f [int][char]$match.Value })";
11952
+ try {
11953
+ const output = await executeFile(win323.join(env.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe"), ["-NoProfile", "-NonInteractive", "-Command", command], env, platform);
11954
+ const resolved = JSON.parse(output.stdout);
11955
+ if (!resolved || typeof resolved.path !== "string" || !win323.isAbsolute(resolved.path) || typeof resolved.pathValue !== "string") {
11956
+ return null;
11957
+ }
11958
+ const inherited = Object.fromEntries(Object.entries(env).filter(([key]) => key.toLowerCase() !== "path"));
11959
+ return { path: resolved.path, env: { ...inherited, PATH: resolved.pathValue } };
11960
+ } catch (error) {
11961
+ if (error instanceof Error && error.name === "TimeoutError")
11962
+ return null;
11963
+ const output = await executeFile(win323.join(env.SystemRoot ?? "C:\\Windows", "System32", "where.exe"), ["aident"], env, platform).catch(() => null);
11964
+ const path = output?.stdout.split(/\r?\n/).find((line) => win323.isAbsolute(line.trim()))?.trim();
11965
+ return path ? { path, env } : null;
11966
+ }
11868
11967
  }
11869
11968
  const shell = env.SHELL && isAbsolute5(env.SHELL) ? env.SHELL : "/bin/sh";
11870
11969
  if (!SUPPORTED_SHELLS.has(basename3(shell)))
11871
11970
  return resolveFromPath(env, platform);
11872
11971
  const marker = "__AIDENT_ENTRYPOINT__";
11873
11972
  const command = `printf '${marker}%s\\n${marker}%s\\n' "$PATH" "$(command -v aident 2>/dev/null || true)"`;
11874
- const output = await execFile3(shell, ["-lic", command], { env }).catch(() => null);
11973
+ const output = await executeFile(shell, ["-lic", command], env, platform).catch(() => null);
11875
11974
  if (!output)
11876
11975
  return resolveFromPath(env, platform);
11877
11976
  const marked = output.stdout.split(/\r?\n/).filter((line) => line.startsWith(marker)).map((line) => line.slice(marker.length));
@@ -11902,12 +12001,66 @@ async function resolveExecutable(file, env, platform) {
11902
12001
  async function isExecutable2(path, platform) {
11903
12002
  return access2(path, platform === "win32" ? constants3.F_OK : constants3.X_OK).then(() => true, () => false);
11904
12003
  }
11905
- async function readAidentCliVersion(path, env) {
11906
- const result = await runFile(path, ["--version"], env).catch(() => null);
12004
+ async function readAidentCliVersion(path, env, platform, executeFile) {
12005
+ const result = await executeFile(path, ["--version"], env, platform).catch(() => null);
11907
12006
  return result ? parseCliVersionOutput(result.stdout) : null;
11908
12007
  }
11909
- function runFile(file, args, env) {
11910
- return execFile3(file, [...args], { env, windowsHide: true }).then(({ stdout, stderr }) => ({ stdout, stderr }));
12008
+ async function runFile(file, args, env, platform, signal, timeout, spawnProbe = execFileCallback2) {
12009
+ const invocation = getPlatformExecFileInvocation(file, args, platform, env.ComSpec);
12010
+ if (platform !== "win32" || !timeout) {
12011
+ const { stdout, stderr } = await execFile3(invocation.file, invocation.args, { env, windowsHide: true });
12012
+ return { stdout, stderr };
12013
+ }
12014
+ signal?.throwIfAborted();
12015
+ return new Promise((resolve, reject) => {
12016
+ let cancelled = false;
12017
+ let completed = false;
12018
+ const child = spawnProbe(invocation.file, invocation.args, { env, windowsHide: true, encoding: "utf8" }, (error, stdout, stderr) => {
12019
+ completed = true;
12020
+ if (cancelled)
12021
+ return;
12022
+ clearTimeout(timer);
12023
+ signal?.removeEventListener("abort", cancel);
12024
+ if (error)
12025
+ reject(error);
12026
+ else
12027
+ resolve({ stdout, stderr });
12028
+ });
12029
+ const cancel = () => {
12030
+ if (cancelled)
12031
+ return;
12032
+ cancelled = true;
12033
+ clearTimeout(timer);
12034
+ signal?.removeEventListener("abort", cancel);
12035
+ let finished = false;
12036
+ const finish = () => {
12037
+ if (finished)
12038
+ return;
12039
+ finished = true;
12040
+ clearTimeout(cleanupTimer);
12041
+ child.removeListener("close", finish);
12042
+ child.kill();
12043
+ child.stdout?.destroy();
12044
+ child.stderr?.destroy();
12045
+ reject(new DOMException("Windows CLI entrypoint probe timed out", "TimeoutError"));
12046
+ };
12047
+ const cleanupTimeout = Math.min(timeout, WINDOWS_PROBE_CLEANUP_TIMEOUT_MS);
12048
+ const cleanupTimer = setTimeout(finish, cleanupTimeout);
12049
+ if (child.pid) {
12050
+ spawnProbe(win323.join(env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe"), ["/PID", String(child.pid), "/T", "/F"], { windowsHide: true, timeout: cleanupTimeout, encoding: "utf8" }, () => {
12051
+ child.kill();
12052
+ if (completed)
12053
+ finish();
12054
+ else
12055
+ child.once("close", finish);
12056
+ });
12057
+ } else {
12058
+ finish();
12059
+ }
12060
+ };
12061
+ const timer = setTimeout(cancel, timeout);
12062
+ signal?.addEventListener("abort", cancel, { once: true });
12063
+ });
11911
12064
  }
11912
12065
 
11913
12066
  // src/harnesses/registry.ts
@@ -14632,7 +14785,8 @@ var HttpOperationPollingResultDefinitionSchema = objectType({
14632
14785
  path: stringType().startsWith("/"),
14633
14786
  idField: stringType().min(1),
14634
14787
  idPathParam: stringType().min(1).optional(),
14635
- idQueryParam: stringType().min(1).optional()
14788
+ idQueryParam: stringType().min(1).optional(),
14789
+ inputPathParams: arrayType(stringType().min(1)).optional()
14636
14790
  }).superRefine((result, ctx) => {
14637
14791
  if (result.idPathParam === undefined === (result.idQueryParam === undefined)) {
14638
14792
  ctx.addIssue({
@@ -14647,16 +14801,28 @@ var HttpOperationPollingResultDefinitionSchema = objectType({
14647
14801
  path: ["path"]
14648
14802
  });
14649
14803
  }
14804
+ for (const inputPathParam of result.inputPathParams ?? []) {
14805
+ if (!result.path.includes(`{${inputPathParam}}`)) {
14806
+ ctx.addIssue({
14807
+ code: ZodIssueCode.custom,
14808
+ message: "Polling result path must include every input path parameter",
14809
+ path: ["inputPathParams"]
14810
+ });
14811
+ }
14812
+ }
14650
14813
  });
14651
14814
  var HttpOperationPollingDefinitionSchema = objectType({
14652
14815
  path: stringType().startsWith("/"),
14653
14816
  taskIdField: stringType().min(1),
14654
14817
  taskIdPathParam: stringType().min(1).optional(),
14655
14818
  taskIdQueryParam: stringType().min(1).optional(),
14656
- statusField: stringType().min(1),
14657
- pendingStatuses: arrayType(stringType().min(1)).min(1),
14658
- terminalStatuses: arrayType(stringType().min(1)).min(1),
14819
+ inputPathParams: arrayType(stringType().min(1)).optional(),
14820
+ statusField: stringType().min(1).optional(),
14821
+ pendingStatuses: arrayType(stringType().min(1)).min(1).optional(),
14822
+ terminalStatuses: arrayType(stringType().min(1)).min(1).optional(),
14659
14823
  failureStatuses: arrayType(stringType().min(1)).optional(),
14824
+ successField: stringType().min(1).optional(),
14825
+ failureField: stringType().min(1).optional(),
14660
14826
  intervalMs: numberType().int().min(HTTP_OPERATION_MIN_POLL_INTERVAL_MS),
14661
14827
  timeoutMs: numberType().int().min(HTTP_OPERATION_MIN_TIMEOUT_MS).max(HTTP_OPERATION_MAX_TIMEOUT_MS),
14662
14828
  result: HttpOperationPollingResultDefinitionSchema.optional()
@@ -14674,7 +14840,38 @@ var HttpOperationPollingDefinitionSchema = objectType({
14674
14840
  path: ["path"]
14675
14841
  });
14676
14842
  }
14677
- const terminalStatuses = new Set(polling.terminalStatuses);
14843
+ for (const inputPathParam of polling.inputPathParams ?? []) {
14844
+ if (!polling.path.includes(`{${inputPathParam}}`)) {
14845
+ ctx.addIssue({
14846
+ code: ZodIssueCode.custom,
14847
+ message: "Polling path must include every input path parameter",
14848
+ path: ["inputPathParams"]
14849
+ });
14850
+ }
14851
+ }
14852
+ const usesStatus = polling.statusField !== undefined;
14853
+ const usesFields = polling.successField !== undefined || polling.failureField !== undefined;
14854
+ if (usesStatus === usesFields) {
14855
+ ctx.addIssue({
14856
+ code: ZodIssueCode.custom,
14857
+ message: "Polling must define either status fields or success and failure fields"
14858
+ });
14859
+ }
14860
+ if (usesStatus && (!polling.pendingStatuses || !polling.terminalStatuses)) {
14861
+ ctx.addIssue({
14862
+ code: ZodIssueCode.custom,
14863
+ message: "Status polling requires pending and terminal statuses",
14864
+ path: ["statusField"]
14865
+ });
14866
+ }
14867
+ if (usesFields && (!polling.successField || !polling.failureField)) {
14868
+ ctx.addIssue({
14869
+ code: ZodIssueCode.custom,
14870
+ message: "Field polling requires success and failure fields",
14871
+ path: ["successField"]
14872
+ });
14873
+ }
14874
+ const terminalStatuses = new Set(polling.terminalStatuses ?? []);
14678
14875
  if (polling.failureStatuses?.some((status) => !terminalStatuses.has(status))) {
14679
14876
  ctx.addIssue({
14680
14877
  code: ZodIssueCode.custom,
@@ -16977,6 +17174,10 @@ var LOADOUT_ONBOARDING_GUIDE_MESSAGES = {
16977
17174
  completedTitle: "Run another task",
16978
17175
  description: "Choose your AI app, then enter a task.",
16979
17176
  exploreDescription: `Use ${LOADOUT_APPS_LABEL} for research, content, video, and more.`,
17177
+ hostedAction: "Try the hosted agent",
17178
+ hostedDescription: "Run a real task on your phone. No agent installation needed.",
17179
+ hostedTitle: "Try Loadout on your phone",
17180
+ returnToHostedResult: "Return to your hosted result",
16980
17181
  placeholder: "Enter a task...",
16981
17182
  title: "Run a task with Loadout",
16982
17183
  tryTool: `Explore ready-to-use ${LOADOUT_APPS_LABEL}`
@@ -17034,6 +17235,10 @@ var LOADOUT_ONBOARDING_GUIDE_MESSAGES = {
17034
17235
  completedTitle: "再运行一个任务",
17035
17236
  description: "选择你的 AI 应用,然后输入任务。",
17036
17237
  exploreDescription: `使用${chineseTerminology.apps}完成研究、内容、视频等任务。`,
17238
+ hostedAction: "试用托管智能体",
17239
+ hostedDescription: "在手机上运行真实任务,无需安装智能体。",
17240
+ hostedTitle: "在手机上试用 Loadout",
17241
+ returnToHostedResult: "返回托管任务结果",
17037
17242
  placeholder: "输入任务…",
17038
17243
  title: "使用 Loadout 运行任务",
17039
17244
  tryTool: `探索即开即用的${chineseTerminology.apps}`
@@ -17091,6 +17296,10 @@ var LOADOUT_ONBOARDING_GUIDE_MESSAGES = {
17091
17296
  completedTitle: "Ejecuta otra tarea",
17092
17297
  description: "Elige tu app de IA e introduce una tarea.",
17093
17298
  exploreDescription: `Usa ${LOADOUT_APPS_LABEL} para investigación, contenido, video y más.`,
17299
+ hostedAction: "Probar el agente alojado",
17300
+ hostedDescription: "Ejecuta una tarea real en tu móvil sin instalar nada en un agente.",
17301
+ hostedTitle: "Prueba Loadout en tu móvil",
17302
+ returnToHostedResult: "Volver al resultado del agente",
17094
17303
  placeholder: "Introduce una tarea...",
17095
17304
  title: "Ejecuta una tarea con Loadout",
17096
17305
  tryTool: `Explora ${LOADOUT_APPS_LABEL} listas para usar`
@@ -17148,6 +17357,10 @@ var LOADOUT_ONBOARDING_GUIDE_MESSAGES = {
17148
17357
  completedTitle: "Exécuter une autre tâche",
17149
17358
  description: "Choisissez votre app IA, puis saisissez une tâche.",
17150
17359
  exploreDescription: `Utilisez des ${LOADOUT_APPS_LABEL} pour la recherche, le contenu, la vidéo et plus encore.`,
17360
+ hostedAction: "Essayer l’agent hébergé",
17361
+ hostedDescription: "Exécutez une vraie tâche sur votre téléphone, sans installer d’agent.",
17362
+ hostedTitle: "Essayez Loadout sur votre téléphone",
17363
+ returnToHostedResult: "Revenir au résultat de l’agent",
17151
17364
  placeholder: "Saisissez une tâche...",
17152
17365
  title: "Exécuter une tâche avec Loadout",
17153
17366
  tryTool: `Explorer les ${LOADOUT_APPS_LABEL} prêtes à l’emploi`
@@ -17205,6 +17418,10 @@ var LOADOUT_ONBOARDING_GUIDE_MESSAGES = {
17205
17418
  completedTitle: "別のタスクを実行",
17206
17419
  description: "AIアプリを選び、タスクを入力します。",
17207
17420
  exploreDescription: `リサーチ、コンテンツ、動画などに${LOADOUT_APPS_LABEL}を活用できます。`,
17421
+ hostedAction: "ホスト型エージェントを試す",
17422
+ hostedDescription: "エージェントをインストールせずに、スマートフォンで実際のタスクを実行できます。",
17423
+ hostedTitle: "スマートフォンでLoadoutを試す",
17424
+ returnToHostedResult: "ホスト型エージェントの結果に戻る",
17208
17425
  placeholder: "タスクを入力...",
17209
17426
  title: "Loadoutでタスクを実行",
17210
17427
  tryTool: `すぐに使える${LOADOUT_APPS_LABEL}を探す`
@@ -17262,6 +17479,10 @@ var LOADOUT_ONBOARDING_GUIDE_MESSAGES = {
17262
17479
  completedTitle: "एक और टास्क चलाएं",
17263
17480
  description: "अपना AI ऐप चुनें, फिर एक टास्क दर्ज करें।",
17264
17481
  exploreDescription: `रिसर्च, कंटेंट, वीडियो और अन्य काम के लिए ${LOADOUT_APPS_LABEL} इस्तेमाल करें।`,
17482
+ hostedAction: "होस्टेड एजेंट आज़माएं",
17483
+ hostedDescription: "बिना एजेंट इंस्टॉल किए फ़ोन पर असली टास्क चलाएं।",
17484
+ hostedTitle: "फ़ोन पर Loadout आज़माएं",
17485
+ returnToHostedResult: "होस्टेड परिणाम पर वापस जाएं",
17265
17486
  placeholder: "टास्क दर्ज करें...",
17266
17487
  title: "Loadout से टास्क चलाएं",
17267
17488
  tryTool: `तैयार ${LOADOUT_APPS_LABEL} देखें`
@@ -18683,7 +18904,7 @@ async function stageOnboardingSkill(home, artifact) {
18683
18904
 
18684
18905
  // src/onboarding/transaction.ts
18685
18906
  import { randomUUID as randomUUID8 } from "node:crypto";
18686
- import { cp, lstat as lstat11, mkdir as mkdir12, readdir as readdir6, readFile as readFile17, rename as rename8, rm as rm7, symlink as symlink5 } from "node:fs/promises";
18907
+ import { cp as cp2, lstat as lstat11, mkdir as mkdir12, readdir as readdir6, readFile as readFile17, rename as rename8, rm as rm7, symlink as symlink5 } from "node:fs/promises";
18687
18908
  import { dirname as dirname11, join as join16, relative as relative5 } from "node:path";
18688
18909
  async function applyOnboardingPlan(plan, options) {
18689
18910
  if (options.confirmationDigest !== plan.confirmationDigest)
@@ -18851,7 +19072,7 @@ async function applyChange(change, desiredContent, options) {
18851
19072
  if (change.action === "copy" /* Copy */) {
18852
19073
  const temporary = `${change.target}.${randomUUID8()}.tmp`;
18853
19074
  await mkdir12(dirname11(temporary), { recursive: true, mode: 448 });
18854
- await cp(options.skillSourceDirectory, temporary, { recursive: true, errorOnExist: true });
19075
+ await cp2(options.skillSourceDirectory, temporary, { recursive: true, errorOnExist: true });
18855
19076
  await rename8(temporary, change.target);
18856
19077
  return;
18857
19078
  }
@@ -19002,7 +19223,7 @@ class OnboardingService {
19002
19223
  }
19003
19224
  }
19004
19225
 
19005
- // ../web/shared/loadout/LoadoutOnboardingTelemetry.ts
19226
+ // ../web/shared/loadout/LoadoutOnboardingTelemetryContract.ts
19006
19227
  var LoadoutOnboardingPath;
19007
19228
  ((LoadoutOnboardingPath) => {
19008
19229
  LoadoutOnboardingPath["Unselected"] = "unselected";
@@ -19019,6 +19240,8 @@ var LoadoutOnboardingTelemetryStage;
19019
19240
  LoadoutOnboardingTelemetryStage["LoginCompleted"] = "login_completed";
19020
19241
  LoadoutOnboardingTelemetryStage["MethodSelected"] = "method_selected";
19021
19242
  LoadoutOnboardingTelemetryStage["NativeDownloadStarted"] = "native_download_started";
19243
+ LoadoutOnboardingTelemetryStage["PackageInstallStarted"] = "package_install_started";
19244
+ LoadoutOnboardingTelemetryStage["PackageInstallCompleted"] = "package_install_completed";
19022
19245
  LoadoutOnboardingTelemetryStage["DesktopStarted"] = "desktop_started";
19023
19246
  LoadoutOnboardingTelemetryStage["InstallationCompleted"] = "installation_completed";
19024
19247
  LoadoutOnboardingTelemetryStage["OnboardingStarted"] = "onboarding_started";
@@ -19055,6 +19278,8 @@ var LoadoutOnboardingLaunchSource;
19055
19278
  LoadoutOnboardingLaunchSource["Desktop"] = "desktop";
19056
19279
  LoadoutOnboardingLaunchSource["Web"] = "web";
19057
19280
  })(LoadoutOnboardingLaunchSource ||= {});
19281
+
19282
+ // ../web/shared/loadout/LoadoutOnboardingTelemetry.ts
19058
19283
  var LoadoutOnboardingClientFamily;
19059
19284
  ((LoadoutOnboardingClientFamily) => {
19060
19285
  LoadoutOnboardingClientFamily["Aident"] = "aident";
@@ -19168,6 +19393,8 @@ function recordLoadoutOnboardingTelemetry(baseUrl, event, fetchImpl = fetch) {
19168
19393
  genRecordLoadoutOnboardingTelemetry(baseUrl, event, fetchImpl);
19169
19394
  }
19170
19395
  async function genRecordLoadoutOnboardingTelemetry(baseUrl, event, fetchImpl = fetch) {
19396
+ if (process.env.AIDENT_DISABLE_ONBOARDING_TELEMETRY === "1")
19397
+ return false;
19171
19398
  const endpoint = `${baseUrl.replace(/\/+$/, "")}/.well-known/aident-onboarding-event`;
19172
19399
  if (!isTrustedAidentMetadataUrl(endpoint))
19173
19400
  return false;
@@ -19204,11 +19431,22 @@ var UI_FILES = ["index.html", "app.js", "style.css"];
19204
19431
  var MAX_UI_BYTES = 5 * 1024 * 1024;
19205
19432
  async function resolveOnboardingUiAssets(baseUrl, cliVersion, home) {
19206
19433
  const cacheRoot = join17(home, ".aident", "ui");
19207
- return Promise.race([resolveOnlineOrCached(baseUrl, cliVersion, cacheRoot), resolveTimeout(2000)]);
19434
+ const controller = new AbortController;
19435
+ const fallback = new Promise((resolve) => {
19436
+ controller.signal.addEventListener("abort", () => resolve(null), { once: true });
19437
+ });
19438
+ const timeout = setTimeout(() => controller.abort(), 2000);
19439
+ timeout.unref();
19440
+ try {
19441
+ return await Promise.race([resolveOnlineOrCached(baseUrl, cliVersion, cacheRoot, controller.signal), fallback]);
19442
+ } finally {
19443
+ clearTimeout(timeout);
19444
+ controller.abort();
19445
+ }
19208
19446
  }
19209
- async function resolveOnlineOrCached(baseUrl, cliVersion, cacheRoot) {
19447
+ async function resolveOnlineOrCached(baseUrl, cliVersion, cacheRoot, signal) {
19210
19448
  try {
19211
- const metadata = await fetchLoadoutSkillMetadata(baseUrl);
19449
+ const metadata = await fetchLoadoutSkillMetadata(baseUrl, signal);
19212
19450
  const policy = metadata?.updatePolicy;
19213
19451
  const descriptor = policy?.schemaVersion === 2 ? policy.onboardingUi : undefined;
19214
19452
  if (!descriptor || !isCompatible(descriptor, cliVersion))
@@ -19216,7 +19454,7 @@ async function resolveOnlineOrCached(baseUrl, cliVersion, cacheRoot) {
19216
19454
  if (!isTrustedAidentMetadataUrl(descriptor.url) || new URL(descriptor.url).origin !== new URL(baseUrl).origin) {
19217
19455
  return loadCachedUi(cacheRoot, cliVersion);
19218
19456
  }
19219
- const response = await fetch(descriptor.url, { redirect: "error" });
19457
+ const response = await fetch(descriptor.url, { redirect: "error", signal });
19220
19458
  if (!response.ok)
19221
19459
  return loadCachedUi(cacheRoot, cliVersion);
19222
19460
  const bytes = await readBoundedResponseBody(response, descriptor.byteLength, MAX_UI_BYTES, "onboarding-ui-response-too-large");
@@ -19229,12 +19467,6 @@ async function resolveOnlineOrCached(baseUrl, cliVersion, cacheRoot) {
19229
19467
  return loadCachedUi(cacheRoot, cliVersion);
19230
19468
  }
19231
19469
  }
19232
- function resolveTimeout(milliseconds) {
19233
- return new Promise((resolve) => {
19234
- const timer = setTimeout(() => resolve(null), milliseconds);
19235
- timer.unref();
19236
- });
19237
- }
19238
19470
  function verifyArtifact(bytes, descriptor) {
19239
19471
  return descriptor.byteLength <= MAX_UI_BYTES && bytes.byteLength === descriptor.byteLength && createHash11("sha256").update(bytes).digest("hex") === descriptor.sha256 && verifyMinisign(bytes, descriptor.signature, descriptor.signingKeyId);
19240
19472
  }