@finchtech/cli 0.3.3 → 0.3.4

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/README.md +120 -112
  2. package/dist/finch.js +621 -503
  3. package/package.json +4 -4
package/dist/finch.js CHANGED
@@ -6606,11 +6606,478 @@ var require_ajv = __commonJS({
6606
6606
  }
6607
6607
  });
6608
6608
 
6609
+ // src/state.ts
6610
+ import { execFile } from "node:child_process";
6611
+ import { constants } from "node:fs";
6612
+ import {
6613
+ chmod,
6614
+ lstat,
6615
+ mkdir,
6616
+ open,
6617
+ readFile,
6618
+ realpath,
6619
+ rename,
6620
+ rm
6621
+ } from "node:fs/promises";
6622
+ import {
6623
+ dirname,
6624
+ isAbsolute,
6625
+ join,
6626
+ normalize,
6627
+ parse,
6628
+ relative,
6629
+ resolve,
6630
+ sep
6631
+ } from "node:path";
6632
+ import { homedir } from "node:os";
6633
+ import { promisify } from "node:util";
6634
+ function resolveFinchtechHome(environment3, userHome = homedir()) {
6635
+ const explicit = environment3.FINCHTECH_HOME;
6636
+ if (typeof explicit === "string" && explicit.length > 0) return explicit;
6637
+ return resolve(userHome, ".finchtech");
6638
+ }
6639
+ function windowsSystemExecutable(name) {
6640
+ const systemRoot = process.env.SystemRoot;
6641
+ if (typeof systemRoot !== "string" || systemRoot.length === 0 || !isAbsolute(systemRoot)) {
6642
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6643
+ }
6644
+ return join(systemRoot, "System32", name);
6645
+ }
6646
+ async function queryCurrentWindowsSid() {
6647
+ try {
6648
+ const { stdout } = await execFileAsync(
6649
+ windowsSystemExecutable("whoami.exe"),
6650
+ ["/user", "/fo", "csv", "/nh"],
6651
+ { windowsHide: true }
6652
+ );
6653
+ const match = /,"(S-[0-9-]+)"\s*$/u.exec(stdout.trim());
6654
+ if (!match?.[1]) throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6655
+ return match[1];
6656
+ } catch (cause) {
6657
+ if (cause instanceof CliStateError) throw cause;
6658
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
6659
+ }
6660
+ }
6661
+ function currentWindowsSid() {
6662
+ windowsSidPromise ??= queryCurrentWindowsSid();
6663
+ return windowsSidPromise;
6664
+ }
6665
+ async function hardenWindows(path2, directory) {
6666
+ const sid = await currentWindowsSid();
6667
+ const permission = directory ? `*${sid}:(OI)(CI)F` : `*${sid}:F`;
6668
+ try {
6669
+ await execFileAsync(
6670
+ windowsSystemExecutable("icacls.exe"),
6671
+ [path2, "/setowner", `*${sid}`],
6672
+ { windowsHide: true }
6673
+ );
6674
+ await execFileAsync(
6675
+ windowsSystemExecutable("icacls.exe"),
6676
+ [path2, "/inheritance:r", "/grant:r", permission],
6677
+ { windowsHide: true }
6678
+ );
6679
+ } catch (cause) {
6680
+ if (cause instanceof CliStateError) throw cause;
6681
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
6682
+ }
6683
+ }
6684
+ async function verifyWindowsAcl(path2) {
6685
+ const expectedSid = await currentWindowsSid();
6686
+ try {
6687
+ const arguments_ = ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", inspectWindowsAclScript];
6688
+ const options = {
6689
+ env: { ...process.env, FINCHTECH_ACL_PATH: path2 },
6690
+ windowsHide: true
6691
+ };
6692
+ let stdout;
6693
+ try {
6694
+ ({ stdout } = await execFileAsync("pwsh.exe", arguments_, options));
6695
+ } catch {
6696
+ ({ stdout } = await execFileAsync("powershell.exe", arguments_, options));
6697
+ }
6698
+ const inspection = JSON.parse(stdout);
6699
+ const unrelatedRule = inspection.rules.some((rule) => rule.sid !== expectedSid && !WINDOWS_PLATFORM_ADMINISTRATOR_SIDS.has(rule.sid) || rule.type !== "Allow");
6700
+ if (inspection.ownerSid !== expectedSid || !inspection.protected || inspection.rules.length < 1 || inspection.rules.some((rule) => rule.inherited) || unrelatedRule) {
6701
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6702
+ }
6703
+ } catch (cause) {
6704
+ if (cause instanceof CliStateError) throw cause;
6705
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
6706
+ }
6707
+ }
6708
+ async function harden(path2, directory) {
6709
+ if (process.platform === "win32") {
6710
+ await hardenWindows(path2, directory);
6711
+ } else {
6712
+ await chmod(path2, directory ? 448 : 384);
6713
+ }
6714
+ }
6715
+ async function writeExclusiveProtectedFile(path2, value) {
6716
+ const target = resolve(path2);
6717
+ const parent = dirname(target);
6718
+ if (!isAbsolute(path2) || parse(target).root === target) {
6719
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6720
+ }
6721
+ let handle;
6722
+ let created = false;
6723
+ try {
6724
+ await assertSafePathComponents(parent);
6725
+ await defaultPermissionPolicy.verify(parent, true);
6726
+ handle = await open(target, "wx", 384);
6727
+ created = true;
6728
+ const opened = await handle.stat();
6729
+ const pathAfterOpen = await lstat(target);
6730
+ if (!opened.isFile() || opened.nlink !== 1 || !sameIdentity(opened, pathAfterOpen)) {
6731
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6732
+ }
6733
+ await defaultPermissionPolicy.hardenNew(target, false);
6734
+ await defaultPermissionPolicy.verify(target, false);
6735
+ const openedAfterAcl = await handle.stat();
6736
+ const pathAfterAcl = await lstat(target);
6737
+ if (!sameIdentity(opened, openedAfterAcl) || !sameIdentity(opened, pathAfterAcl) || openedAfterAcl.nlink !== 1) {
6738
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6739
+ }
6740
+ await handle.writeFile(value);
6741
+ await handle.sync();
6742
+ await handle.close();
6743
+ handle = void 0;
6744
+ await defaultSyncDirectory(parent);
6745
+ } catch (cause) {
6746
+ await handle?.close().catch(() => void 0);
6747
+ if (cause.code === "EEXIST") {
6748
+ throw new CliStateError("CLI_STATE_CONFLICT", { cause });
6749
+ }
6750
+ if (created) {
6751
+ await rm(target, { force: true }).catch(() => void 0);
6752
+ await defaultSyncDirectory(parent).catch(() => void 0);
6753
+ }
6754
+ throw permissionFailure(cause);
6755
+ }
6756
+ }
6757
+ async function assertSafeNode(path2, directory) {
6758
+ const metadata = await lstat(path2);
6759
+ if (metadata.isSymbolicLink() || (directory ? !metadata.isDirectory() : !metadata.isFile())) {
6760
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6761
+ }
6762
+ if (!directory && metadata.nlink !== 1) {
6763
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6764
+ }
6765
+ if (process.platform !== "win32") {
6766
+ const expected = directory ? 448 : 384;
6767
+ if ((metadata.mode & 511) !== expected || typeof process.getuid === "function" && metadata.uid !== process.getuid()) {
6768
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6769
+ }
6770
+ } else {
6771
+ await verifyWindowsAcl(path2);
6772
+ }
6773
+ }
6774
+ async function defaultSyncDirectory(path2) {
6775
+ const handle = await open(path2, process.platform === "win32" ? "r+" : "r");
6776
+ try {
6777
+ await handle.sync();
6778
+ } finally {
6779
+ await handle.close();
6780
+ }
6781
+ }
6782
+ function samePath(left, right) {
6783
+ const normalizedLeft = normalize(left);
6784
+ const normalizedRight = normalize(right);
6785
+ return process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight;
6786
+ }
6787
+ async function assertSafePathComponents(path2) {
6788
+ const target = resolve(path2);
6789
+ const root = parse(target).root;
6790
+ let cursor = root;
6791
+ for (const component of relative(root, target).split(sep).filter(Boolean)) {
6792
+ cursor = resolve(cursor, component);
6793
+ const metadata = await lstat(cursor);
6794
+ if (metadata.isSymbolicLink()) {
6795
+ const darwinSystemTarget = process.platform === "darwin" ? DARWIN_SYSTEM_LINK_TARGETS.get(cursor) : void 0;
6796
+ if (darwinSystemTarget === void 0 || !samePath(await realpath(cursor), darwinSystemTarget)) {
6797
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6798
+ }
6799
+ }
6800
+ }
6801
+ }
6802
+ function permissionFailure(cause) {
6803
+ return cause instanceof CliStateError ? cause : new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
6804
+ }
6805
+ function sameIdentity(left, right) {
6806
+ return left.dev === right.dev && left.ino === right.ino;
6807
+ }
6808
+ async function readProtectedFile(path2, maximumBytes) {
6809
+ const target = resolve(path2);
6810
+ const parent = dirname(target);
6811
+ if (!isAbsolute(path2) || !Number.isInteger(maximumBytes) || maximumBytes < 1) {
6812
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6813
+ }
6814
+ let handle;
6815
+ try {
6816
+ await assertSafePathComponents(parent);
6817
+ await defaultPermissionPolicy.verify(parent, true);
6818
+ const before = await lstat(target);
6819
+ const noFollow = constants.O_NOFOLLOW ?? 0;
6820
+ handle = await open(target, constants.O_RDONLY | noFollow);
6821
+ const opened = await handle.stat();
6822
+ const afterOpen = await lstat(target);
6823
+ if (!sameIdentity(before, opened) || !sameIdentity(opened, afterOpen) || !opened.isFile() || opened.nlink !== 1 || opened.size < 1 || opened.size > maximumBytes) {
6824
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6825
+ }
6826
+ if (process.platform === "win32") {
6827
+ await verifyWindowsAcl(target);
6828
+ } else if ((opened.mode & 511) !== 384 || typeof process.getuid === "function" && opened.uid !== process.getuid()) {
6829
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6830
+ }
6831
+ const afterAcl = await lstat(target);
6832
+ if (!sameIdentity(opened, afterAcl)) throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6833
+ const body = Buffer.alloc(opened.size);
6834
+ let offset = 0;
6835
+ while (offset < body.length) {
6836
+ const chunk = await handle.read(body, offset, body.length - offset, offset);
6837
+ if (chunk.bytesRead === 0) throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6838
+ offset += chunk.bytesRead;
6839
+ }
6840
+ const stable = await handle.stat();
6841
+ if (!sameIdentity(opened, stable) || stable.size !== opened.size) {
6842
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6843
+ }
6844
+ return body;
6845
+ } catch (cause) {
6846
+ throw permissionFailure(cause);
6847
+ } finally {
6848
+ await handle?.close().catch(() => void 0);
6849
+ }
6850
+ }
6851
+ var execFileAsync, DARWIN_SYSTEM_LINK_TARGETS, CliStateError, windowsSidPromise, WINDOWS_PLATFORM_ADMINISTRATOR_SIDS, inspectWindowsAclScript, defaultPermissionPolicy, HardenedFileStore;
6852
+ var init_state = __esm({
6853
+ "src/state.ts"() {
6854
+ "use strict";
6855
+ execFileAsync = promisify(execFile);
6856
+ DARWIN_SYSTEM_LINK_TARGETS = /* @__PURE__ */ new Map([
6857
+ ["/etc", "/private/etc"],
6858
+ ["/tmp", "/private/tmp"],
6859
+ ["/var", "/private/var"]
6860
+ ]);
6861
+ CliStateError = class extends Error {
6862
+ code;
6863
+ diagnostic;
6864
+ constructor(code, options) {
6865
+ super(code, options);
6866
+ this.name = "CliStateError";
6867
+ this.code = code;
6868
+ this.diagnostic = options?.diagnostic ?? null;
6869
+ }
6870
+ };
6871
+ WINDOWS_PLATFORM_ADMINISTRATOR_SIDS = /* @__PURE__ */ new Set([
6872
+ "S-1-5-18",
6873
+ // LocalSystem
6874
+ "S-1-5-32-544"
6875
+ // BUILTIN\Administrators
6876
+ ]);
6877
+ inspectWindowsAclScript = String.raw`
6878
+ $ErrorActionPreference = 'Stop'
6879
+ $acl = Get-Acl -LiteralPath $env:FINCHTECH_ACL_PATH
6880
+ $ownerSid = ([System.Security.Principal.NTAccount]$acl.Owner).Translate([System.Security.Principal.SecurityIdentifier]).Value
6881
+ $rules = @($acl.Access | ForEach-Object {
6882
+ $sid = $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value
6883
+ [pscustomobject]@{ sid = $sid; inherited = $_.IsInherited; type = $_.AccessControlType.ToString() }
6884
+ })
6885
+ [pscustomobject]@{ ownerSid = $ownerSid; protected = $acl.AreAccessRulesProtected; rules = $rules } |
6886
+ ConvertTo-Json -Compress -Depth 4
6887
+ `;
6888
+ defaultPermissionPolicy = Object.freeze({
6889
+ hardenNew: harden,
6890
+ verify: assertSafeNode
6891
+ });
6892
+ HardenedFileStore = class {
6893
+ root;
6894
+ permissions;
6895
+ syncDirectory;
6896
+ constructor(root, options = {}) {
6897
+ const resolved = resolve(root);
6898
+ if (!isAbsolute(root) || samePath(resolved, parse(resolved).root)) {
6899
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6900
+ }
6901
+ this.root = resolved;
6902
+ this.permissions = options.permissions ?? defaultPermissionPolicy;
6903
+ this.syncDirectory = options.syncDirectory ?? defaultSyncDirectory;
6904
+ }
6905
+ resolveRelative(path2) {
6906
+ if (path2.length === 0 || isAbsolute(path2) || path2.includes("\0")) {
6907
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6908
+ }
6909
+ const target = resolve(this.root, path2);
6910
+ const fromRoot = relative(this.root, target);
6911
+ if (fromRoot === ".." || fromRoot.startsWith(`..${sep}`) || isAbsolute(fromRoot)) {
6912
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6913
+ }
6914
+ return target;
6915
+ }
6916
+ async ensureDirectory(path2) {
6917
+ try {
6918
+ const metadata = await lstat(path2);
6919
+ if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
6920
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6921
+ }
6922
+ await assertSafePathComponents(path2);
6923
+ await this.permissions.verify(path2, true);
6924
+ return;
6925
+ } catch (cause) {
6926
+ if (cause.code !== "ENOENT") throw permissionFailure(cause);
6927
+ }
6928
+ const parent = dirname(path2);
6929
+ await assertSafePathComponents(parent);
6930
+ let created = false;
6931
+ try {
6932
+ await mkdir(path2, { recursive: false, mode: 448 });
6933
+ created = true;
6934
+ await this.permissions.hardenNew(path2, true);
6935
+ await this.permissions.verify(path2, true);
6936
+ await this.syncDirectory(parent);
6937
+ } catch (cause) {
6938
+ if (created) {
6939
+ await rm(path2, { recursive: true, force: true }).catch(() => void 0);
6940
+ await this.syncDirectory(parent).catch(() => void 0);
6941
+ }
6942
+ throw permissionFailure(cause);
6943
+ }
6944
+ }
6945
+ async ensureParent(target) {
6946
+ await this.ensureDirectory(this.root);
6947
+ const parent = resolve(target, "..");
6948
+ const fromRoot = relative(this.root, parent);
6949
+ let cursor = this.root;
6950
+ for (const component of fromRoot.split(sep).filter(Boolean)) {
6951
+ cursor = resolve(cursor, component);
6952
+ await this.ensureDirectory(cursor);
6953
+ }
6954
+ }
6955
+ async writeSecret(path2, value, options) {
6956
+ if (!options.exclusive) throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
6957
+ const target = this.resolveRelative(path2);
6958
+ await this.ensureParent(target);
6959
+ let handle;
6960
+ let created = false;
6961
+ try {
6962
+ handle = await open(target, "wx", 384);
6963
+ created = true;
6964
+ await this.permissions.hardenNew(target, false);
6965
+ await this.permissions.verify(target, false);
6966
+ await handle.writeFile(value);
6967
+ await handle.sync();
6968
+ await handle.close();
6969
+ handle = void 0;
6970
+ await this.syncDirectory(dirname(target));
6971
+ } catch (cause) {
6972
+ await handle?.close().catch(() => void 0);
6973
+ if (cause.code === "EEXIST") {
6974
+ throw new CliStateError("CLI_STATE_CONFLICT", { cause });
6975
+ }
6976
+ if (created) {
6977
+ await rm(target, { force: true }).catch(() => void 0);
6978
+ await this.syncDirectory(dirname(target)).catch(() => void 0);
6979
+ }
6980
+ throw permissionFailure(cause);
6981
+ }
6982
+ }
6983
+ async writeJson(path2, value) {
6984
+ const target = this.resolveRelative(path2);
6985
+ const temporary = `${target}.tmp-${process.pid}-${Date.now()}`;
6986
+ await this.ensureParent(target);
6987
+ try {
6988
+ await this.writeSecret(relative(this.root, temporary), Buffer.from(`${JSON.stringify(value)}
6989
+ `, "utf8"), {
6990
+ exclusive: true
6991
+ });
6992
+ try {
6993
+ await lstat(target);
6994
+ await this.permissions.verify(target, false);
6995
+ } catch (cause) {
6996
+ if (cause.code !== "ENOENT") throw cause;
6997
+ }
6998
+ await rename(temporary, target);
6999
+ await this.permissions.verify(target, false);
7000
+ await this.syncDirectory(dirname(target));
7001
+ } catch (cause) {
7002
+ await rm(temporary, { force: true }).catch(() => void 0);
7003
+ throw cause;
7004
+ }
7005
+ }
7006
+ async readSecret(path2) {
7007
+ const target = this.resolveRelative(path2);
7008
+ try {
7009
+ await this.permissions.verify(target, false);
7010
+ return await readFile(target);
7011
+ } catch (cause) {
7012
+ if (cause.code === "ENOENT") {
7013
+ throw new CliStateError("CLI_STATE_NOT_FOUND", { cause });
7014
+ }
7015
+ if (cause instanceof CliStateError) throw cause;
7016
+ throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
7017
+ }
7018
+ }
7019
+ async readJson(path2) {
7020
+ try {
7021
+ return JSON.parse((await this.readSecret(path2)).toString("utf8"));
7022
+ } catch (cause) {
7023
+ if (cause instanceof CliStateError && cause.code !== "CLI_STATE_CORRUPT") throw cause;
7024
+ throw new CliStateError("CLI_STATE_CORRUPT", { cause });
7025
+ }
7026
+ }
7027
+ async remove(path2, options = {}) {
7028
+ const target = this.resolveRelative(path2);
7029
+ await rm(target, { force: true, recursive: options.recursive ?? false });
7030
+ await this.syncDirectory(dirname(target)).catch((cause) => {
7031
+ if (cause.code !== "ENOENT") throw cause;
7032
+ });
7033
+ }
7034
+ };
7035
+ }
7036
+ });
7037
+
6609
7038
  // src/cli-error.ts
7039
+ function localFailureMessage(error51) {
7040
+ if (error51 instanceof CliStateError) {
7041
+ if (error51.code === "CLI_STATE_PERMISSIONS_INVALID") {
7042
+ return process.platform === "win32" ? "Protected local state has unsafe ownership or ACLs. Restrict it to the current Windows user and retry." : "Protected local state has unsafe permissions. Directories must be mode 0700 and files mode 0600.";
7043
+ }
7044
+ if (error51.diagnostic === "profile") {
7045
+ return error51.code === "CLI_STATE_NOT_FOUND" ? "Finch endpoint override is missing. Remove the override to use PROD, or provision protected FINCHTECH_HOME/profile.json." : "Finch endpoint override is invalid. Remove it to use PROD, or reprovision protected FINCHTECH_HOME/profile.json.";
7046
+ }
7047
+ if (error51.diagnostic === "wallet_bootstrap") {
7048
+ return "Local authorization remains without a wallet. Restore the original wallet state, or run finch logout --local to discard local authorization before importing.";
7049
+ }
7050
+ if (error51.diagnostic === "wallet") {
7051
+ return error51.code === "CLI_STATE_NOT_FOUND" ? "No current wallet was found in FINCHTECH_HOME (default: ~/.finchtech). Run finch wallet create, or finch wallet use --file <PATH> to import an existing private key into a new wallet store." : "Local wallet state in FINCHTECH_HOME (default: ~/.finchtech) is incomplete or invalid. Restore the wallet files from backup; do not overwrite the existing wallet.";
7052
+ }
7053
+ if (error51.diagnostic === "session") {
7054
+ return error51.code === "CLI_STATE_NOT_FOUND" ? "No active Finch login was found. Run finch login --help for supported authentication chains, then finch login --chain-id <CHAIN_ID>." : "The active Finch login is invalid. Run finch logout --local, then finch login --help to select an authentication chain and log in again.";
7055
+ }
7056
+ if (error51.code === "CLI_STATE_NOT_FOUND") {
7057
+ return "Required protected local state is missing. Check the command prerequisites and retry.";
7058
+ }
7059
+ if (error51.code === "CLI_STATE_CONFLICT") {
7060
+ return "Protected local state already exists or changed concurrently. Refresh state and retry.";
7061
+ }
7062
+ return "Protected local state is corrupt. Recreate the affected profile, login, grant, or recovery state.";
7063
+ }
7064
+ if (error51.message === "FINCHTECH_HOME is required.") {
7065
+ return "FINCHTECH_HOME is required. Set it to the protected Finch Agent CLI state directory.";
7066
+ }
7067
+ const missingScope = /^No active MCP grant with ([a-z_:]+) is available for this account and endpoint\.$/u.exec(error51.message);
7068
+ if (missingScope !== null) {
7069
+ return `Required MCP grant scope ${missingScope[1]} is missing. Create or refresh that scoped grant.`;
7070
+ }
7071
+ if (/^The selected .* MCP grant is invalid\.$/u.test(error51.message)) {
7072
+ return "The selected MCP grant is invalid. Recreate the grant for the active account and endpoint.";
7073
+ }
7074
+ return "Command input does not match the expected schema. Check finch help for this command.";
7075
+ }
6610
7076
  var CliAuthorityError, CliProductionUnavailableError, BrowserOpenError, PUBLIC_RECOVERY_COMMAND, CliRecoveryBlockedError, CliSkillSourceError, CliSkillPublicationError;
6611
7077
  var init_cli_error = __esm({
6612
7078
  "src/cli-error.ts"() {
6613
7079
  "use strict";
7080
+ init_state();
6614
7081
  CliAuthorityError = class extends Error {
6615
7082
  code = "MCP_CLI_WALLET_MISMATCH";
6616
7083
  constructor(code = "MCP_CLI_WALLET_MISMATCH") {
@@ -14454,11 +14921,13 @@ function assertSortedIdentityScopes(scopes) {
14454
14921
  previous = scope;
14455
14922
  }
14456
14923
  }
14457
- var identityScopeValues, IDENTITY_SCOPE_ORDER_CONTRACT, identityEnvironmentValues, environmentSchema, chainIdSchema, dateTimeSchema, grantScopesSchema, identityChallengeRequestSchema, identityChallengeResponseSchema, identityLoginRequestSchema, identitySessionResponseSchema, identitySessionStatusResponseSchema, identityLogoutRequestSchema, identityMcpActorResponseSchema, identityGrantCreateRequestSchema, identityGrantCreateResponseSchema, identityGrantShowResponseSchema, identityGrantRevokeRequestSchema, identityErrorCodes, identityErrorEnvelopeSchema, IDENTITY_V1_ROUTES, IDENTITY_V1_OPERATIONS, IDENTITY_MCP_V1_MANIFEST, CLI_GRANT_SHOW_WIRE;
14924
+ var PRODUCTION_AUTH_CHAIN_IDS, TEST_AUTH_CHAIN_IDS, identityScopeValues, IDENTITY_SCOPE_ORDER_CONTRACT, identityEnvironmentValues, environmentSchema, chainIdSchema, dateTimeSchema, grantScopesSchema, identityChallengeRequestSchema, identityChallengeResponseSchema, identityLoginRequestSchema, identitySessionResponseSchema, identitySessionStatusResponseSchema, identityLogoutRequestSchema, identityMcpActorResponseSchema, identityGrantCreateRequestSchema, identityGrantCreateResponseSchema, identityGrantShowResponseSchema, identityGrantRevokeRequestSchema, identityErrorCodes, identityErrorEnvelopeSchema, IDENTITY_V1_ROUTES, IDENTITY_V1_OPERATIONS, IDENTITY_MCP_V1_MANIFEST, CLI_GRANT_SHOW_WIRE;
14458
14925
  var init_identity = __esm({
14459
14926
  "../../packages/agent-market-contracts/src/v1/identity.ts"() {
14460
14927
  "use strict";
14461
14928
  init_common2();
14929
+ PRODUCTION_AUTH_CHAIN_IDS = Object.freeze([1, 10, 56, 8453, 42161]);
14930
+ TEST_AUTH_CHAIN_IDS = Object.freeze([84532]);
14462
14931
  identityScopeValues = [
14463
14932
  "account:read",
14464
14933
  "agent_market:buyer:invoke",
@@ -15025,7 +15494,7 @@ var init_merchant = __esm({
15025
15494
  });
15026
15495
  publicationOperationResultSchema = strictObjectSchema({
15027
15496
  serviceVersionId: canonicalUuidSchema,
15028
- publicationGeneration: nonNegativeGenerationSchema,
15497
+ publicationGeneration: { anyOf: [nonNegativeGenerationSchema, nullSchema3] },
15029
15498
  activeVersionRevision: positiveRevisionSchema
15030
15499
  });
15031
15500
  localPurchaseOperationResultSchema = strictObjectSchema({
@@ -16915,6 +17384,7 @@ var init_schemas = __esm({
16915
17384
  perPage: { type: "integer", minimum: 1, maximum: 100 }
16916
17385
  }, []);
16917
17386
  createTaskDraftRequestSchema = strictObjectSchema2({
17387
+ expectedUpdatedAt: { type: "string", format: "date-time" },
16918
17388
  title: { type: "string", minLength: 1, maxLength: 80 },
16919
17389
  description: { type: "string", minLength: 1, maxLength: 500 },
16920
17390
  goal: { type: "string", minLength: 1, maxLength: 2e3 },
@@ -16940,7 +17410,7 @@ var init_schemas = __esm({
16940
17410
  startAt: nullableString,
16941
17411
  goals: { type: "array", maxItems: 5, items: openObjectSchema },
16942
17412
  publishNow: { const: false }
16943
- }, ["title", "description", "goal", "rewardAmount", "publishNow"]);
17413
+ }, ["title", "goal", "rewardAmount", "publishNow"]);
16944
17414
  taskSubmissionRequestSchema = strictObjectSchema2({
16945
17415
  content: { type: "string", minLength: 1, maxLength: 1e5 },
16946
17416
  proofUrl: nullableString,
@@ -18051,7 +18521,7 @@ var init_errors2 = __esm({
18051
18521
  });
18052
18522
 
18053
18523
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/parse.js
18054
- var _parse, parse, _parseAsync, parseAsync, _safeParse, safeParse, _safeParseAsync, safeParseAsync, _encode, encode5, _decode, decode7, _encodeAsync, encodeAsync, _decodeAsync, decodeAsync, _safeEncode, safeEncode, _safeDecode, safeDecode, _safeEncodeAsync, safeEncodeAsync, _safeDecodeAsync, safeDecodeAsync;
18524
+ var _parse, parse2, _parseAsync, parseAsync, _safeParse, safeParse, _safeParseAsync, safeParseAsync, _encode, encode5, _decode, decode7, _encodeAsync, encodeAsync, _decodeAsync, decodeAsync, _safeEncode, safeEncode, _safeDecode, safeDecode, _safeEncodeAsync, safeEncodeAsync, _safeDecodeAsync, safeDecodeAsync;
18055
18525
  var init_parse = __esm({
18056
18526
  "../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/parse.js"() {
18057
18527
  init_core();
@@ -18070,7 +18540,7 @@ var init_parse = __esm({
18070
18540
  }
18071
18541
  return result2.value;
18072
18542
  };
18073
- parse = /* @__PURE__ */ _parse($ZodRealError);
18543
+ parse2 = /* @__PURE__ */ _parse($ZodRealError);
18074
18544
  _parseAsync = (_Err) => async (schema4, value, _ctx, params) => {
18075
18545
  const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
18076
18546
  let result2 = schema4._zod.run({ value, issues: [] }, ctx);
@@ -20916,10 +21386,10 @@ var init_schemas2 = __esm({
20916
21386
  throw new Error("implement() must be called with a function");
20917
21387
  }
20918
21388
  return function(...args) {
20919
- const parsedArgs = inst._def.input ? parse(inst._def.input, args) : args;
21389
+ const parsedArgs = inst._def.input ? parse2(inst._def.input, args) : args;
20920
21390
  const result2 = Reflect.apply(func, this, parsedArgs);
20921
21391
  if (inst._def.output) {
20922
- return parse(inst._def.output, result2);
21392
+ return parse2(inst._def.output, result2);
20923
21393
  }
20924
21394
  return result2;
20925
21395
  };
@@ -29714,7 +30184,7 @@ __export(core_exports2, {
29714
30184
  isValidJWT: () => isValidJWT,
29715
30185
  locales: () => locales_exports,
29716
30186
  meta: () => meta,
29717
- parse: () => parse,
30187
+ parse: () => parse2,
29718
30188
  parseAsync: () => parseAsync,
29719
30189
  prettifyError: () => prettifyError,
29720
30190
  process: () => process2,
@@ -29889,12 +30359,12 @@ var init_errors3 = __esm({
29889
30359
  });
29890
30360
 
29891
30361
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/parse.js
29892
- var parse2, parseAsync2, safeParse2, safeParseAsync2, encode6, decode8, encodeAsync2, decodeAsync2, safeEncode2, safeDecode2, safeEncodeAsync2, safeDecodeAsync2;
30362
+ var parse3, parseAsync2, safeParse2, safeParseAsync2, encode6, decode8, encodeAsync2, decodeAsync2, safeEncode2, safeDecode2, safeEncodeAsync2, safeDecodeAsync2;
29893
30363
  var init_parse2 = __esm({
29894
30364
  "../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/parse.js"() {
29895
30365
  init_core2();
29896
30366
  init_errors3();
29897
- parse2 = /* @__PURE__ */ _parse(ZodRealError);
30367
+ parse3 = /* @__PURE__ */ _parse(ZodRealError);
29898
30368
  parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
29899
30369
  safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
29900
30370
  safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
@@ -30614,7 +31084,7 @@ var init_schemas3 = __esm({
30614
31084
  inst.def = def;
30615
31085
  inst.type = def.type;
30616
31086
  Object.defineProperty(inst, "_def", { value: def });
30617
- inst.parse = (data, params) => parse2(inst, data, params, { callee: inst.parse });
31087
+ inst.parse = (data, params) => parse3(inst, data, params, { callee: inst.parse });
30618
31088
  inst.safeParse = (data, params) => safeParse2(inst, data, params);
30619
31089
  inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
30620
31090
  inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
@@ -32114,7 +32584,7 @@ __export(external_exports, {
32114
32584
  object: () => object,
32115
32585
  optional: () => optional,
32116
32586
  overwrite: () => _overwrite,
32117
- parse: () => parse2,
32587
+ parse: () => parse3,
32118
32588
  parseAsync: () => parseAsync2,
32119
32589
  partialRecord: () => partialRecord,
32120
32590
  pipe: () => pipe,
@@ -33160,7 +33630,7 @@ var init_web_localization = __esm({
33160
33630
  languageSwitchEnabled: true
33161
33631
  },
33162
33632
  production: {
33163
- languageSwitchEnabled: false
33633
+ languageSwitchEnabled: true
33164
33634
  }
33165
33635
  }
33166
33636
  };
@@ -54610,6 +55080,7 @@ var init_api2 = __esm({
54610
55080
  ajv = new import_ajv.default({ allErrors: true, strict: false });
54611
55081
  ajv.addFormat("date-time", (value) => Number.isFinite(Date.parse(value)));
54612
55082
  ajv.addFormat("uri", (value) => URL.canParse(value));
55083
+ ajv.addFormat("uuid", new RegExp(canonicalUuidPattern, "u"));
54613
55084
  validatorCache = /* @__PURE__ */ new WeakMap();
54614
55085
  skillUnsignedIntentResponseSchema = {
54615
55086
  type: "object",
@@ -55576,435 +56047,6 @@ var init_accounts = __esm({
55576
56047
  }
55577
56048
  });
55578
56049
 
55579
- // src/state.ts
55580
- import { execFile } from "node:child_process";
55581
- import { constants } from "node:fs";
55582
- import {
55583
- chmod,
55584
- lstat,
55585
- mkdir,
55586
- open,
55587
- readFile,
55588
- realpath,
55589
- rename,
55590
- rm
55591
- } from "node:fs/promises";
55592
- import {
55593
- dirname,
55594
- isAbsolute,
55595
- join,
55596
- normalize,
55597
- parse as parse3,
55598
- relative,
55599
- resolve,
55600
- sep
55601
- } from "node:path";
55602
- import { homedir } from "node:os";
55603
- import { promisify } from "node:util";
55604
- function resolveFinchtechHome(environment3, userHome = homedir()) {
55605
- const explicit = environment3.FINCHTECH_HOME;
55606
- if (typeof explicit === "string" && explicit.length > 0) return explicit;
55607
- return resolve(userHome, ".finchtech");
55608
- }
55609
- function windowsSystemExecutable(name) {
55610
- const systemRoot = process.env.SystemRoot;
55611
- if (typeof systemRoot !== "string" || systemRoot.length === 0 || !isAbsolute(systemRoot)) {
55612
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55613
- }
55614
- return join(systemRoot, "System32", name);
55615
- }
55616
- async function queryCurrentWindowsSid() {
55617
- try {
55618
- const { stdout } = await execFileAsync(
55619
- windowsSystemExecutable("whoami.exe"),
55620
- ["/user", "/fo", "csv", "/nh"],
55621
- { windowsHide: true }
55622
- );
55623
- const match = /,"(S-[0-9-]+)"\s*$/u.exec(stdout.trim());
55624
- if (!match?.[1]) throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55625
- return match[1];
55626
- } catch (cause) {
55627
- if (cause instanceof CliStateError) throw cause;
55628
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
55629
- }
55630
- }
55631
- function currentWindowsSid() {
55632
- windowsSidPromise ??= queryCurrentWindowsSid();
55633
- return windowsSidPromise;
55634
- }
55635
- async function hardenWindows(path2, directory) {
55636
- const sid = await currentWindowsSid();
55637
- const permission = directory ? `*${sid}:(OI)(CI)F` : `*${sid}:F`;
55638
- try {
55639
- await execFileAsync(
55640
- windowsSystemExecutable("icacls.exe"),
55641
- [path2, "/setowner", `*${sid}`],
55642
- { windowsHide: true }
55643
- );
55644
- await execFileAsync(
55645
- windowsSystemExecutable("icacls.exe"),
55646
- [path2, "/inheritance:r", "/grant:r", permission],
55647
- { windowsHide: true }
55648
- );
55649
- } catch (cause) {
55650
- if (cause instanceof CliStateError) throw cause;
55651
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
55652
- }
55653
- }
55654
- async function verifyWindowsAcl(path2) {
55655
- const expectedSid = await currentWindowsSid();
55656
- try {
55657
- const arguments_ = ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", inspectWindowsAclScript];
55658
- const options = {
55659
- env: { ...process.env, FINCHTECH_ACL_PATH: path2 },
55660
- windowsHide: true
55661
- };
55662
- let stdout;
55663
- try {
55664
- ({ stdout } = await execFileAsync("pwsh.exe", arguments_, options));
55665
- } catch {
55666
- ({ stdout } = await execFileAsync("powershell.exe", arguments_, options));
55667
- }
55668
- const inspection = JSON.parse(stdout);
55669
- const unrelatedRule = inspection.rules.some((rule) => rule.sid !== expectedSid && !WINDOWS_PLATFORM_ADMINISTRATOR_SIDS.has(rule.sid) || rule.type !== "Allow");
55670
- if (inspection.ownerSid !== expectedSid || !inspection.protected || inspection.rules.length < 1 || inspection.rules.some((rule) => rule.inherited) || unrelatedRule) {
55671
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55672
- }
55673
- } catch (cause) {
55674
- if (cause instanceof CliStateError) throw cause;
55675
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
55676
- }
55677
- }
55678
- async function harden(path2, directory) {
55679
- if (process.platform === "win32") {
55680
- await hardenWindows(path2, directory);
55681
- } else {
55682
- await chmod(path2, directory ? 448 : 384);
55683
- }
55684
- }
55685
- async function writeExclusiveProtectedFile(path2, value) {
55686
- const target = resolve(path2);
55687
- const parent = dirname(target);
55688
- if (!isAbsolute(path2) || parse3(target).root === target) {
55689
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55690
- }
55691
- let handle;
55692
- let created = false;
55693
- try {
55694
- await assertSafePathComponents(parent);
55695
- await defaultPermissionPolicy.verify(parent, true);
55696
- handle = await open(target, "wx", 384);
55697
- created = true;
55698
- const opened = await handle.stat();
55699
- const pathAfterOpen = await lstat(target);
55700
- if (!opened.isFile() || opened.nlink !== 1 || !sameIdentity(opened, pathAfterOpen)) {
55701
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55702
- }
55703
- await defaultPermissionPolicy.hardenNew(target, false);
55704
- await defaultPermissionPolicy.verify(target, false);
55705
- const openedAfterAcl = await handle.stat();
55706
- const pathAfterAcl = await lstat(target);
55707
- if (!sameIdentity(opened, openedAfterAcl) || !sameIdentity(opened, pathAfterAcl) || openedAfterAcl.nlink !== 1) {
55708
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55709
- }
55710
- await handle.writeFile(value);
55711
- await handle.sync();
55712
- await handle.close();
55713
- handle = void 0;
55714
- await defaultSyncDirectory(parent);
55715
- } catch (cause) {
55716
- await handle?.close().catch(() => void 0);
55717
- if (cause.code === "EEXIST") {
55718
- throw new CliStateError("CLI_STATE_CONFLICT", { cause });
55719
- }
55720
- if (created) {
55721
- await rm(target, { force: true }).catch(() => void 0);
55722
- await defaultSyncDirectory(parent).catch(() => void 0);
55723
- }
55724
- throw permissionFailure(cause);
55725
- }
55726
- }
55727
- async function assertSafeNode(path2, directory) {
55728
- const metadata = await lstat(path2);
55729
- if (metadata.isSymbolicLink() || (directory ? !metadata.isDirectory() : !metadata.isFile())) {
55730
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55731
- }
55732
- if (!directory && metadata.nlink !== 1) {
55733
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55734
- }
55735
- if (process.platform !== "win32") {
55736
- const expected = directory ? 448 : 384;
55737
- if ((metadata.mode & 511) !== expected || typeof process.getuid === "function" && metadata.uid !== process.getuid()) {
55738
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55739
- }
55740
- } else {
55741
- await verifyWindowsAcl(path2);
55742
- }
55743
- }
55744
- async function defaultSyncDirectory(path2) {
55745
- const handle = await open(path2, process.platform === "win32" ? "r+" : "r");
55746
- try {
55747
- await handle.sync();
55748
- } finally {
55749
- await handle.close();
55750
- }
55751
- }
55752
- function samePath(left, right) {
55753
- const normalizedLeft = normalize(left);
55754
- const normalizedRight = normalize(right);
55755
- return process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight;
55756
- }
55757
- async function assertSafePathComponents(path2) {
55758
- const target = resolve(path2);
55759
- const root = parse3(target).root;
55760
- let cursor = root;
55761
- for (const component of relative(root, target).split(sep).filter(Boolean)) {
55762
- cursor = resolve(cursor, component);
55763
- const metadata = await lstat(cursor);
55764
- if (metadata.isSymbolicLink()) {
55765
- const darwinSystemTarget = process.platform === "darwin" ? DARWIN_SYSTEM_LINK_TARGETS.get(cursor) : void 0;
55766
- if (darwinSystemTarget === void 0 || !samePath(await realpath(cursor), darwinSystemTarget)) {
55767
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55768
- }
55769
- }
55770
- }
55771
- }
55772
- function permissionFailure(cause) {
55773
- return cause instanceof CliStateError ? cause : new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
55774
- }
55775
- function sameIdentity(left, right) {
55776
- return left.dev === right.dev && left.ino === right.ino;
55777
- }
55778
- async function readProtectedFile(path2, maximumBytes) {
55779
- const target = resolve(path2);
55780
- const parent = dirname(target);
55781
- if (!isAbsolute(path2) || !Number.isInteger(maximumBytes) || maximumBytes < 1) {
55782
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55783
- }
55784
- let handle;
55785
- try {
55786
- await assertSafePathComponents(parent);
55787
- await defaultPermissionPolicy.verify(parent, true);
55788
- const before = await lstat(target);
55789
- const noFollow = constants.O_NOFOLLOW ?? 0;
55790
- handle = await open(target, constants.O_RDONLY | noFollow);
55791
- const opened = await handle.stat();
55792
- const afterOpen = await lstat(target);
55793
- if (!sameIdentity(before, opened) || !sameIdentity(opened, afterOpen) || !opened.isFile() || opened.nlink !== 1 || opened.size < 1 || opened.size > maximumBytes) {
55794
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55795
- }
55796
- if (process.platform === "win32") {
55797
- await verifyWindowsAcl(target);
55798
- } else if ((opened.mode & 511) !== 384 || typeof process.getuid === "function" && opened.uid !== process.getuid()) {
55799
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55800
- }
55801
- const afterAcl = await lstat(target);
55802
- if (!sameIdentity(opened, afterAcl)) throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55803
- const body = Buffer.alloc(opened.size);
55804
- let offset = 0;
55805
- while (offset < body.length) {
55806
- const chunk = await handle.read(body, offset, body.length - offset, offset);
55807
- if (chunk.bytesRead === 0) throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55808
- offset += chunk.bytesRead;
55809
- }
55810
- const stable = await handle.stat();
55811
- if (!sameIdentity(opened, stable) || stable.size !== opened.size) {
55812
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55813
- }
55814
- return body;
55815
- } catch (cause) {
55816
- throw permissionFailure(cause);
55817
- } finally {
55818
- await handle?.close().catch(() => void 0);
55819
- }
55820
- }
55821
- var execFileAsync, DARWIN_SYSTEM_LINK_TARGETS, CliStateError, windowsSidPromise, WINDOWS_PLATFORM_ADMINISTRATOR_SIDS, inspectWindowsAclScript, defaultPermissionPolicy, HardenedFileStore;
55822
- var init_state = __esm({
55823
- "src/state.ts"() {
55824
- "use strict";
55825
- execFileAsync = promisify(execFile);
55826
- DARWIN_SYSTEM_LINK_TARGETS = /* @__PURE__ */ new Map([
55827
- ["/etc", "/private/etc"],
55828
- ["/tmp", "/private/tmp"],
55829
- ["/var", "/private/var"]
55830
- ]);
55831
- CliStateError = class extends Error {
55832
- code;
55833
- diagnostic;
55834
- constructor(code, options) {
55835
- super(code, options);
55836
- this.name = "CliStateError";
55837
- this.code = code;
55838
- this.diagnostic = options?.diagnostic ?? null;
55839
- }
55840
- };
55841
- WINDOWS_PLATFORM_ADMINISTRATOR_SIDS = /* @__PURE__ */ new Set([
55842
- "S-1-5-18",
55843
- // LocalSystem
55844
- "S-1-5-32-544"
55845
- // BUILTIN\Administrators
55846
- ]);
55847
- inspectWindowsAclScript = String.raw`
55848
- $ErrorActionPreference = 'Stop'
55849
- $acl = Get-Acl -LiteralPath $env:FINCHTECH_ACL_PATH
55850
- $ownerSid = ([System.Security.Principal.NTAccount]$acl.Owner).Translate([System.Security.Principal.SecurityIdentifier]).Value
55851
- $rules = @($acl.Access | ForEach-Object {
55852
- $sid = $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value
55853
- [pscustomobject]@{ sid = $sid; inherited = $_.IsInherited; type = $_.AccessControlType.ToString() }
55854
- })
55855
- [pscustomobject]@{ ownerSid = $ownerSid; protected = $acl.AreAccessRulesProtected; rules = $rules } |
55856
- ConvertTo-Json -Compress -Depth 4
55857
- `;
55858
- defaultPermissionPolicy = Object.freeze({
55859
- hardenNew: harden,
55860
- verify: assertSafeNode
55861
- });
55862
- HardenedFileStore = class {
55863
- root;
55864
- permissions;
55865
- syncDirectory;
55866
- constructor(root, options = {}) {
55867
- const resolved = resolve(root);
55868
- if (!isAbsolute(root) || samePath(resolved, parse3(resolved).root)) {
55869
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55870
- }
55871
- this.root = resolved;
55872
- this.permissions = options.permissions ?? defaultPermissionPolicy;
55873
- this.syncDirectory = options.syncDirectory ?? defaultSyncDirectory;
55874
- }
55875
- resolveRelative(path2) {
55876
- if (path2.length === 0 || isAbsolute(path2) || path2.includes("\0")) {
55877
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55878
- }
55879
- const target = resolve(this.root, path2);
55880
- const fromRoot = relative(this.root, target);
55881
- if (fromRoot === ".." || fromRoot.startsWith(`..${sep}`) || isAbsolute(fromRoot)) {
55882
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55883
- }
55884
- return target;
55885
- }
55886
- async ensureDirectory(path2) {
55887
- try {
55888
- const metadata = await lstat(path2);
55889
- if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
55890
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55891
- }
55892
- await assertSafePathComponents(path2);
55893
- await this.permissions.verify(path2, true);
55894
- return;
55895
- } catch (cause) {
55896
- if (cause.code !== "ENOENT") throw permissionFailure(cause);
55897
- }
55898
- const parent = dirname(path2);
55899
- await assertSafePathComponents(parent);
55900
- let created = false;
55901
- try {
55902
- await mkdir(path2, { recursive: false, mode: 448 });
55903
- created = true;
55904
- await this.permissions.hardenNew(path2, true);
55905
- await this.permissions.verify(path2, true);
55906
- await this.syncDirectory(parent);
55907
- } catch (cause) {
55908
- if (created) {
55909
- await rm(path2, { recursive: true, force: true }).catch(() => void 0);
55910
- await this.syncDirectory(parent).catch(() => void 0);
55911
- }
55912
- throw permissionFailure(cause);
55913
- }
55914
- }
55915
- async ensureParent(target) {
55916
- await this.ensureDirectory(this.root);
55917
- const parent = resolve(target, "..");
55918
- const fromRoot = relative(this.root, parent);
55919
- let cursor = this.root;
55920
- for (const component of fromRoot.split(sep).filter(Boolean)) {
55921
- cursor = resolve(cursor, component);
55922
- await this.ensureDirectory(cursor);
55923
- }
55924
- }
55925
- async writeSecret(path2, value, options) {
55926
- if (!options.exclusive) throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID");
55927
- const target = this.resolveRelative(path2);
55928
- await this.ensureParent(target);
55929
- let handle;
55930
- let created = false;
55931
- try {
55932
- handle = await open(target, "wx", 384);
55933
- created = true;
55934
- await this.permissions.hardenNew(target, false);
55935
- await this.permissions.verify(target, false);
55936
- await handle.writeFile(value);
55937
- await handle.sync();
55938
- await handle.close();
55939
- handle = void 0;
55940
- await this.syncDirectory(dirname(target));
55941
- } catch (cause) {
55942
- await handle?.close().catch(() => void 0);
55943
- if (cause.code === "EEXIST") {
55944
- throw new CliStateError("CLI_STATE_CONFLICT", { cause });
55945
- }
55946
- if (created) {
55947
- await rm(target, { force: true }).catch(() => void 0);
55948
- await this.syncDirectory(dirname(target)).catch(() => void 0);
55949
- }
55950
- throw permissionFailure(cause);
55951
- }
55952
- }
55953
- async writeJson(path2, value) {
55954
- const target = this.resolveRelative(path2);
55955
- const temporary = `${target}.tmp-${process.pid}-${Date.now()}`;
55956
- await this.ensureParent(target);
55957
- try {
55958
- await this.writeSecret(relative(this.root, temporary), Buffer.from(`${JSON.stringify(value)}
55959
- `, "utf8"), {
55960
- exclusive: true
55961
- });
55962
- try {
55963
- await lstat(target);
55964
- await this.permissions.verify(target, false);
55965
- } catch (cause) {
55966
- if (cause.code !== "ENOENT") throw cause;
55967
- }
55968
- await rename(temporary, target);
55969
- await this.permissions.verify(target, false);
55970
- await this.syncDirectory(dirname(target));
55971
- } catch (cause) {
55972
- await rm(temporary, { force: true }).catch(() => void 0);
55973
- throw cause;
55974
- }
55975
- }
55976
- async readSecret(path2) {
55977
- const target = this.resolveRelative(path2);
55978
- try {
55979
- await this.permissions.verify(target, false);
55980
- return await readFile(target);
55981
- } catch (cause) {
55982
- if (cause.code === "ENOENT") {
55983
- throw new CliStateError("CLI_STATE_NOT_FOUND", { cause });
55984
- }
55985
- if (cause instanceof CliStateError) throw cause;
55986
- throw new CliStateError("CLI_STATE_PERMISSIONS_INVALID", { cause });
55987
- }
55988
- }
55989
- async readJson(path2) {
55990
- try {
55991
- return JSON.parse((await this.readSecret(path2)).toString("utf8"));
55992
- } catch (cause) {
55993
- if (cause instanceof CliStateError && cause.code !== "CLI_STATE_CORRUPT") throw cause;
55994
- throw new CliStateError("CLI_STATE_CORRUPT", { cause });
55995
- }
55996
- }
55997
- async remove(path2, options = {}) {
55998
- const target = this.resolveRelative(path2);
55999
- await rm(target, { force: true, recursive: options.recursive ?? false });
56000
- await this.syncDirectory(dirname(target)).catch((cause) => {
56001
- if (cause.code !== "ENOENT") throw cause;
56002
- });
56003
- }
56004
- };
56005
- }
56006
- });
56007
-
56008
56050
  // src/skill-local-flow.ts
56009
56051
  import { createHash } from "node:crypto";
56010
56052
  import { lstat as lstat2, readFile as readFile2, readdir } from "node:fs/promises";
@@ -58100,32 +58142,89 @@ var init_application = __esm({
58100
58142
  return result2;
58101
58143
  }
58102
58144
  async doctorMcp() {
58103
- const profile = await this.authorizedProfile();
58104
- const session = await this.networkSession(profile);
58105
- const wallet = await required3(this.state.readWalletAddress, "state.readWalletAddress").call(this.state);
58106
- const remoteSession = asObject(await required3(this.api.getSession, "api.getSession").call(this.api, session.rawBearer), "MCP doctor Session");
58107
- if (remoteSession.sessionId !== session.sessionId || remoteSession.accountId !== session.accountId || remoteSession.walletAddress !== session.walletAddress || wallet.walletAddress !== session.walletAddress || remoteSession.environment !== session.environment || remoteSession.audience !== "agent_cli" || typeof remoteSession.sessionExpiresAt !== "string" || Date.parse(remoteSession.sessionExpiresAt) <= Date.now()) {
58108
- throw new TypeError("MCP doctor Session authority does not match the active wallet and profile.");
58109
- }
58110
- const mcp = await required3(this.api.inspectRemoteMcp, "api.inspectRemoteMcp").call(this.api);
58111
- if (mcp.url !== `${profile.origin}/mcp` || mcp.authorization !== "oauth" || JSON.stringify(mcp.supportedClients) !== JSON.stringify(["codex"])) {
58112
- throw new TypeError("MCP doctor discovery does not match the authoritative profile.");
58113
- }
58145
+ const inspect = async (run, guidance) => {
58146
+ try {
58147
+ const value = await run();
58148
+ return [value, { status: "ok" }];
58149
+ } catch (cause) {
58150
+ if (cause instanceof CliStateError) {
58151
+ return [null, {
58152
+ status: cause.code === "CLI_STATE_NOT_FOUND" ? "missing" : "invalid",
58153
+ message: localFailureMessage(cause)
58154
+ }];
58155
+ } else if (cause instanceof CliHttpError) {
58156
+ return [null, { status: cause.status === 401 || cause.status === 403 || cause.code === "CLI_RESPONSE_INVALID" ? "invalid" : "unavailable", message: guidance }];
58157
+ } else if (cause instanceof TypeError || cause instanceof CliAuthorityError) {
58158
+ return [null, { status: "invalid", message: guidance }];
58159
+ } else {
58160
+ throw cause;
58161
+ }
58162
+ }
58163
+ };
58164
+ const [profile, endpointCheck] = await inspect(
58165
+ () => this.authorizedProfile(),
58166
+ "Check the protected FINCHTECH_HOME/profile.json endpoint override."
58167
+ );
58168
+ const [wallet, walletCheck] = await inspect(
58169
+ () => required3(this.state.readWalletAddress, "state.readWalletAddress").call(this.state),
58170
+ "Restore the current wallet from backup."
58171
+ );
58172
+ const [session, sessionCheck] = await inspect(
58173
+ () => required3(this.state.readSession, "state.readSession").call(this.state),
58174
+ "Run finch logout --local, then finch login --help to log in again."
58175
+ );
58176
+ let remoteSession = null;
58177
+ let remoteSessionCheck;
58178
+ if (profile !== null && session !== null && wallet !== null) {
58179
+ [remoteSession, remoteSessionCheck] = await inspect(async () => {
58180
+ if (session.origin !== profile.origin || wallet.walletAddress !== session.walletAddress) {
58181
+ throw new CliAuthorityError();
58182
+ }
58183
+ const remote = asObject(await required3(this.api.getSession, "api.getSession").call(this.api, session.rawBearer), "MCP doctor Session");
58184
+ if (remote.sessionId !== session.sessionId || remote.accountId !== session.accountId || remote.walletAddress !== session.walletAddress || remote.environment !== session.environment || remote.audience !== "agent_cli" || typeof remote.sessionExpiresAt !== "string" || !Number.isFinite(Date.parse(remote.sessionExpiresAt)) || Date.parse(remote.sessionExpiresAt) <= Date.now()) throw new CliAuthorityError();
58185
+ return { sessionExpiresAt: remote.sessionExpiresAt };
58186
+ }, "Verify the endpoint and current wallet, then run finch login --help to renew the CLI login. If the server is unavailable, retry later.");
58187
+ } else {
58188
+ remoteSessionCheck = { status: "skipped", message: "Requires a valid endpoint, local wallet, and CLI Session." };
58189
+ }
58190
+ let mcp = null;
58191
+ let mcpDiscoveryCheck;
58192
+ if (profile !== null) {
58193
+ [mcp, mcpDiscoveryCheck] = await inspect(async () => {
58194
+ const discovery = await required3(this.api.inspectRemoteMcp, "api.inspectRemoteMcp").call(this.api);
58195
+ if (discovery.url !== `${profile.origin}/mcp` || discovery.authorization !== "oauth" || JSON.stringify(discovery.supportedClients) !== JSON.stringify(["codex"])) {
58196
+ throw new TypeError("MCP discovery authority mismatch.");
58197
+ }
58198
+ return discovery;
58199
+ }, "Check endpoint connectivity and MCP OAuth discovery configuration, then retry finch mcp doctor.");
58200
+ } else {
58201
+ mcpDiscoveryCheck = { status: "skipped", message: "Requires a valid endpoint." };
58202
+ }
58203
+ const checks = {
58204
+ endpoint: endpointCheck,
58205
+ wallet: walletCheck,
58206
+ session: sessionCheck,
58207
+ remoteSession: remoteSessionCheck,
58208
+ mcpDiscovery: mcpDiscoveryCheck
58209
+ };
58114
58210
  return Object.freeze({
58115
- ready: true,
58211
+ ready: Object.values(checks).every((check2) => check2.status === "ok"),
58116
58212
  readinessScope: "cli_session_and_mcp_discovery",
58117
- harnessOAuth: Object.freeze({
58213
+ checks,
58214
+ harnessOAuth: {
58118
58215
  status: "not_checked",
58119
58216
  guidance: "Verify the connected MCP identity with identity_actor_get and compare it with finch status. If the Harness reports missing or expired OAuth credentials, reauthorize through its native MCP login flow."
58120
- }),
58121
- environment: session.environment,
58122
- origin: profile.origin,
58123
- loginChainId: session.loginChainId,
58124
- accountId: session.accountId,
58125
- walletAddress: session.walletAddress,
58126
- sessionId: session.sessionId,
58127
- sessionExpiresAt: remoteSession.sessionExpiresAt,
58128
- mcp
58217
+ },
58218
+ ...profile === null ? {} : { origin: profile.origin },
58219
+ ...wallet === null ? {} : { walletAddress: wallet.walletAddress },
58220
+ ...session === null ? {} : {
58221
+ environment: session.environment,
58222
+ loginChainId: session.loginChainId,
58223
+ accountId: session.accountId,
58224
+ sessionId: session.sessionId
58225
+ },
58226
+ ...remoteSession === null ? {} : { sessionExpiresAt: remoteSession.sessionExpiresAt },
58227
+ ...mcp === null ? {} : { mcp }
58129
58228
  });
58130
58229
  }
58131
58230
  async assertNoBlockingRecoveries() {
@@ -58185,7 +58284,16 @@ var init_application = __esm({
58185
58284
  throw new TypeError("wallet use requires exactly one registered address or --file path.");
58186
58285
  }
58187
58286
  await this.assertNoBlockingRecoveries();
58188
- const current = await required3(this.state.readWallet, "state.readWallet").call(this.state);
58287
+ let current;
58288
+ try {
58289
+ current = await required3(this.state.readWallet, "state.readWallet").call(this.state);
58290
+ } catch (cause) {
58291
+ if (!(cause instanceof CliStateError) || cause.code !== "CLI_STATE_NOT_FOUND" || input.file === void 0) throw cause;
58292
+ const wallets = await required3(this.state.listWalletAddresses, "state.listWalletAddresses").call(this.state);
58293
+ if (wallets.length !== 0) throw new CliStateError("CLI_STATE_CORRUPT", { cause, diagnostic: "wallet" });
58294
+ await required3(this.state.assertWalletBootstrap, "state.assertWalletBootstrap").call(this.state);
58295
+ return required3(this.state.importWallet, "state.importWallet").call(this.state, input.file);
58296
+ }
58189
58297
  const target = input.file === void 0 ? { walletAddress: normalizeCliEvmAddress(input.address, "Wallet address") } : await required3(this.state.importWallet, "state.importWallet").call(this.state, input.file);
58190
58298
  if (!/^0x[0-9a-f]{40}$/u.test(target.walletAddress)) throw new TypeError("Wallet address is invalid.");
58191
58299
  if (input.file === void 0) {
@@ -58937,7 +59045,7 @@ var init_package = __esm({
58937
59045
  "package.json"() {
58938
59046
  package_default = {
58939
59047
  name: "@finchtech/cli",
58940
- version: "0.3.3",
59048
+ version: "0.3.4",
58941
59049
  description: "Finch Agent CLI for wallet-owned marketplace and MCP workflows.",
58942
59050
  type: "module",
58943
59051
  bin: {
@@ -59066,7 +59174,11 @@ async function routeCommand(arguments_, application) {
59066
59174
  return Object.freeze({
59067
59175
  command: "help",
59068
59176
  topic: commandTopic,
59069
- usage: FINCH_COMMAND_USAGE[commandTopic]
59177
+ usage: FINCH_COMMAND_USAGE[commandTopic],
59178
+ ...commandTopic === "login" ? {
59179
+ authenticationChainIds: PRODUCTION_AUTH_CHAIN_IDS,
59180
+ guidance: "Choose an authentication chain, for example finch login --chain-id 56. The server validates support; market transaction chains are independent."
59181
+ } : {}
59070
59182
  });
59071
59183
  }
59072
59184
  const commands = FINCH_COMMANDS.filter((command) => command.startsWith(`${topic} `));
@@ -59422,6 +59534,7 @@ var init_command_router = __esm({
59422
59534
  "src/command-router.ts"() {
59423
59535
  "use strict";
59424
59536
  init_package();
59537
+ init_v1();
59425
59538
  FINCH_COMMANDS = Object.freeze([
59426
59539
  "wallet create",
59427
59540
  "wallet show",
@@ -59899,7 +60012,14 @@ var init_wallet_store = __esm({
59899
60012
  await this.migrateLegacy();
59900
60013
  const current = record4(await this.files.readJson("wallet/current.json"));
59901
60014
  if (current.version !== 1) throw new CliStateError("CLI_STATE_CORRUPT");
59902
- return this.readRegistered(walletAddress(current.walletAddress));
60015
+ try {
60016
+ return await this.readRegistered(walletAddress(current.walletAddress));
60017
+ } catch (cause) {
60018
+ if (cause instanceof CliStateError && cause.code === "CLI_STATE_NOT_FOUND") {
60019
+ throw new CliStateError("CLI_STATE_CORRUPT", { cause });
60020
+ }
60021
+ throw cause;
60022
+ }
59903
60023
  }
59904
60024
  async readCurrentAddress() {
59905
60025
  const current = await this.readCurrent();
@@ -59911,6 +60031,8 @@ var init_wallet_store = __esm({
59911
60031
 
59912
60032
  // src/local-state.ts
59913
60033
  import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
60034
+ import { lstat as lstat3 } from "node:fs/promises";
60035
+ import { join as join3 } from "node:path";
59914
60036
  function object5(value, name) {
59915
60037
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
59916
60038
  throw new CliStateError("CLI_STATE_CORRUPT", { cause: new TypeError(`${name} must be an object.`) });
@@ -60183,10 +60305,27 @@ var init_local_state = __esm({
60183
60305
  await this.wallets.register(input);
60184
60306
  }
60185
60307
  async readWallet() {
60186
- return this.wallets.readCurrent();
60308
+ try {
60309
+ return await this.wallets.readCurrent();
60310
+ } catch (cause) {
60311
+ if (cause instanceof CliStateError) throw new CliStateError(cause.code, { cause, diagnostic: "wallet" });
60312
+ throw cause;
60313
+ }
60187
60314
  }
60188
60315
  async readWalletAddress() {
60189
- return this.wallets.readCurrentAddress();
60316
+ const wallet = await this.readWallet();
60317
+ return Object.freeze({ walletAddress: wallet.walletAddress });
60318
+ }
60319
+ async assertWalletBootstrap() {
60320
+ for (const path2 of ["session", "grants"]) {
60321
+ try {
60322
+ await lstat3(join3(this.files.root, path2));
60323
+ } catch (cause) {
60324
+ if (cause.code === "ENOENT") continue;
60325
+ throw cause;
60326
+ }
60327
+ throw new CliStateError("CLI_STATE_CONFLICT", { diagnostic: "wallet_bootstrap" });
60328
+ }
60190
60329
  }
60191
60330
  async importWallet(path2) {
60192
60331
  return this.wallets.importFromFile(path2);
@@ -61985,7 +62124,7 @@ async function resolveInstalledProfile(readProfile) {
61985
62124
  }
61986
62125
  function commandNeedsHttp(arguments_) {
61987
62126
  if (arguments_.length === 0 || arguments_.length === 1 && ["--help", "help", "--version"].includes(arguments_[0]) || arguments_[0] === "help" && arguments_.length > 1 || arguments_.length > 1 && arguments_.at(-1) === "--help" && arguments_.filter((argument) => argument === "--help").length === 1) return false;
61988
- return arguments_[0] !== "wallet" && !(arguments_.length === 2 && arguments_[0] === "logout" && arguments_[1] === "--local");
62127
+ return (arguments_[0] !== "wallet" || arguments_[1] === "use") && !(arguments_.length === 2 && arguments_[0] === "logout" && arguments_[1] === "--local");
61989
62128
  }
61990
62129
  async function runInstalledCli(arguments_, environment3 = process.env, authorities = {}) {
61991
62130
  const profileFree = arguments_.length === 0 || arguments_.length === 1 && ["--help", "help", "--version"].includes(arguments_[0]) || arguments_[0] === "help" && arguments_.length > 1 || arguments_.length > 1 && arguments_.at(-1) === "--help" && arguments_.filter((argument) => argument === "--help").length === 1;
@@ -61993,7 +62132,14 @@ async function runInstalledCli(arguments_, environment3 = process.env, authoriti
61993
62132
  const home = resolveFinchtechHome(environment3);
61994
62133
  const state = new LocalCliState(home);
61995
62134
  const needsHttp = commandNeedsHttp(arguments_);
61996
- const profile = needsHttp ? await resolveInstalledProfile(async () => state.readProfile()) : void 0;
62135
+ let profile;
62136
+ if (needsHttp) {
62137
+ try {
62138
+ profile = await resolveInstalledProfile(async () => state.readProfile());
62139
+ } catch (cause) {
62140
+ if (!(cause instanceof CliStateError) || arguments_.length !== 2 || arguments_[0] !== "mcp" || arguments_[1] !== "doctor") throw cause;
62141
+ }
62142
+ }
61997
62143
  const api = profile === void 0 ? {} : new AgentCliHttpClient(profile.origin);
61998
62144
  const signer = new InstalledWalletSigner();
61999
62145
  const skillIntentSender = profile === void 0 || arguments_[0] !== "skill" ? void 0 : new InstalledSkillIntentSender();
@@ -62043,37 +62189,6 @@ function remoteExitCode(status) {
62043
62189
  }
62044
62190
  return AGENT_CLI_OUTPUT_CONTRACT.exitCodes.remoteOrProviderUnavailable;
62045
62191
  }
62046
- function localFailureMessage(error51) {
62047
- if (error51 instanceof CliStateError) {
62048
- if (error51.code === "CLI_STATE_PERMISSIONS_INVALID") {
62049
- return process.platform === "win32" ? "Protected local state has unsafe ownership or ACLs. Restrict it to the current Windows user and retry." : "Protected local state has unsafe permissions. Directories must be mode 0700 and files mode 0600.";
62050
- }
62051
- if (error51.diagnostic === "profile") {
62052
- return error51.code === "CLI_STATE_NOT_FOUND" ? "Finch endpoint override is missing. Remove the override to use PROD, or provision protected FINCHTECH_HOME/profile.json." : "Finch endpoint override is invalid. Remove it to use PROD, or reprovision protected FINCHTECH_HOME/profile.json.";
62053
- }
62054
- if (error51.diagnostic === "session") {
62055
- return error51.code === "CLI_STATE_NOT_FOUND" ? "No active Finch login was found. Run finch login --chain-id <SUPPORTED_AUTH_CHAIN_ID>." : "The active Finch login is invalid. Run finch logout --local, then finch login --chain-id <SUPPORTED_AUTH_CHAIN_ID>.";
62056
- }
62057
- if (error51.code === "CLI_STATE_NOT_FOUND") {
62058
- return "Required protected local state is missing. Check the command prerequisites and retry.";
62059
- }
62060
- if (error51.code === "CLI_STATE_CONFLICT") {
62061
- return "Protected local state already exists or changed concurrently. Refresh state and retry.";
62062
- }
62063
- return "Protected local state is corrupt. Recreate the affected profile, login, grant, or recovery state.";
62064
- }
62065
- if (error51.message === "FINCHTECH_HOME is required.") {
62066
- return "FINCHTECH_HOME is required. Set it to the protected Finch Agent CLI state directory.";
62067
- }
62068
- const missingScope = /^No active MCP grant with ([a-z_:]+) is available for this account and endpoint\.$/u.exec(error51.message);
62069
- if (missingScope !== null) {
62070
- return `Required MCP grant scope ${missingScope[1]} is missing. Create or refresh that scoped grant.`;
62071
- }
62072
- if (/^The selected .* MCP grant is invalid\.$/u.test(error51.message)) {
62073
- return "The selected MCP grant is invalid. Recreate the grant for the active account and endpoint.";
62074
- }
62075
- return "Command input does not match the expected schema. Check finch help for this command.";
62076
- }
62077
62192
  function failure(error51) {
62078
62193
  if (error51 instanceof CliSkillSourceError) {
62079
62194
  return {
@@ -62123,6 +62238,9 @@ function emitCliOutcome(outcome, sinks) {
62123
62238
  if (!(outcome instanceof Error)) {
62124
62239
  sinks.stdout(`${JSON.stringify(outcome)}
62125
62240
  `);
62241
+ if (typeof outcome === "object" && outcome !== null && "command" in outcome && outcome.command === "mcp.doctor" && "data" in outcome && typeof outcome.data === "object" && outcome.data !== null && "ready" in outcome.data && outcome.data.ready === false) {
62242
+ return AGENT_CLI_OUTPUT_CONTRACT.exitCodes.usageOrLocalSchema;
62243
+ }
62126
62244
  return AGENT_CLI_OUTPUT_CONTRACT.exitCodes.success;
62127
62245
  }
62128
62246
  if (outcome instanceof CliSkillPublicationError) {