@fluid-app/fluid-cli-connect 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;AAyCA;;iBAAsB,aAAA,CACpB,QAAA,UACA,GAAA,WACA,WAAA,aACC,OAAA;AAAA,iBA+Ca,uBAAA,CACd,MAAA,EAAQ,OAAA,EACR,GAAA,EAAK,cAAA;AAAA,cAyND,MAAA,EAAQ,WAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;AA0CA;;iBAAsB,aAAA,CACpB,QAAA,UACA,GAAA,WACA,WAAA,aACC,OAAA;AAAA,iBA+Ca,uBAAA,CACd,MAAA,EAAQ,OAAA,EACR,GAAA,EAAK,cAAA;AAAA,cAgOD,MAAA,EAAQ,WAAA"}
package/dist/index.mjs CHANGED
@@ -130,11 +130,30 @@ function integrationsBase() {
130
130
  return (process.env["FLUID_INTEGRATIONS_BASE"] ?? "https://fluid-integrations-106074092699.us-west3.run.app").trim().replace(/\/$/, "");
131
131
  }
132
132
  /** The provider's integration as fluid-integrations sees this company. */
133
+ /**
134
+ * fluid-integrations has no record of a company until its first Connect
135
+ * droplet is installed in Fluid, so it answers a new company's token with
136
+ * 409 company_not_installed. Nothing is installed yet, which is what every
137
+ * caller needs to know; installing goes through Fluid, not this service.
138
+ */
139
+ function isCompanyNotInstalled(error) {
140
+ const e = error;
141
+ return e.status === 409 && e.body?.error_code === "company_not_installed";
142
+ }
133
143
  async function providerStatus(integrations, provider) {
134
144
  let wire;
135
145
  try {
136
146
  wire = await integrations.get(`/api/v1/integrations/${provider.slug}`);
137
147
  } catch (error) {
148
+ if (isCompanyNotInstalled(error)) return {
149
+ provider: provider.id,
150
+ label: provider.label,
151
+ installed: false,
152
+ connected: false,
153
+ companyIntegrationId: null,
154
+ dropletUuid: null,
155
+ integrationId: null
156
+ };
138
157
  if (error.status === 404) throw new ConnectError(`${provider.label} isn't available on fluid-integrations for this company.`);
139
158
  throw error;
140
159
  }
@@ -204,7 +223,16 @@ async function updateSyncSettings(integrations, ciId, patch) {
204
223
  return getCompanyIntegration(integrations, ciId);
205
224
  }
206
225
  /** A mapping type as its route names it, e.g. `customer_type`. */
207
- function mappingPath(ciId, type) {
226
+ /**
227
+ * fluid-integrations names its mapping cards with a `_title` suffix
228
+ * (`customer_type_title`); the routes, and so the commands, use the bare type
229
+ * (`customer_type`). Accept either and use the bare one.
230
+ */
231
+ function mappingType(name) {
232
+ return name.replace(/_title$/, "");
233
+ }
234
+ function mappingPath(ciId, name) {
235
+ const type = mappingType(name);
208
236
  if (!/^[a-z_]+$/.test(type)) throw new ConnectError(`"${type}" isn't a mapping type. Use names like customer_type or warehouse (see \`fluid connect show\`).`);
209
237
  return `/api/v1/company_integrations/${ciId}/${type}_mappings`;
210
238
  }
@@ -230,7 +258,7 @@ async function mappingOptions(integrations, ciId, type, provider) {
230
258
  /** Create one mapping row. `fields` are the row's attributes, wrapped in
231
259
  * the singular resource key the API expects (`customer_type_mapping`). */
232
260
  async function createMapping(integrations, ciId, type, fields) {
233
- return integrations.post(mappingPath(ciId, type), { [`${type}_mapping`]: fields });
261
+ return integrations.post(mappingPath(ciId, type), { [`${mappingType(type)}_mapping`]: fields });
234
262
  }
235
263
  async function deleteMapping(integrations, ciId, type, id) {
236
264
  await integrations.delete(`${mappingPath(ciId, type)}/${id}`);
@@ -597,7 +625,7 @@ function errorText(error) {
597
625
  * is an id), so what's returned is what a person still has to decide.
598
626
  */
599
627
  async function unmappedReport(integrations, ciId, provider, autoMap) {
600
- const types = ((await getCompanyIntegration(integrations, ciId)).supported_mappings ?? []).map((card) => card.replace(/_title$/, "")).filter((type) => type in SOURCE_FIELD || LISTS_FLUID_VALUES.has(type));
628
+ const types = ((await getCompanyIntegration(integrations, ciId)).supported_mappings ?? []).map(mappingType).filter((type) => type in SOURCE_FIELD || LISTS_FLUID_VALUES.has(type));
601
629
  const reports = [];
602
630
  for (const type of types) reports.push(await reportFor(integrations, ciId, provider, type, autoMap));
603
631
  return reports;
@@ -1494,7 +1522,10 @@ function registerConnectCommands(parent, ctx) {
1494
1522
  return;
1495
1523
  }
1496
1524
  await confirmChange(`Install ${provider.label} for this company?`, opts.yes);
1497
- ctx.output(await installProvider(await fluidClient(ctx), provider, status.dropletUuid));
1525
+ ctx.output({
1526
+ ...await installProvider(await fluidClient(ctx), provider, status.dropletUuid),
1527
+ next: `The connection appears within a minute. Check with \`fluid connect show ${provider.id}\`, then save the credentials.`
1528
+ });
1498
1529
  }));
1499
1530
  parent.command("show <provider>").description("Show the connection: state, supported mappings, and every sync setting with its current value").action((providerId) => run(async () => {
1500
1531
  const provider = providerOf(providerId);
@@ -1508,7 +1539,7 @@ function registerConnectCommands(parent, ctx) {
1508
1539
  liveSyncState: ci.live_sync_state ?? null,
1509
1540
  liveSyncDegradedReason: ci.live_sync_degraded_reason ?? null,
1510
1541
  credentialGroups: provider.credentialGroups,
1511
- supportedMappings: ci.supported_mappings ?? [],
1542
+ supportedMappings: (ci.supported_mappings ?? []).map(mappingType),
1512
1543
  settings: summarizeSettings(ci)
1513
1544
  });
1514
1545
  }));
@@ -1592,7 +1623,7 @@ function registerConnectCommands(parent, ctx) {
1592
1623
  }
1593
1624
  const plugin = {
1594
1625
  name: "@fluid-app/fluid-cli-connect",
1595
- version: "0.1.0",
1626
+ version: "0.1.1",
1596
1627
  register(ctx) {
1597
1628
  ctx.program.addCommand(createDomainCommand("connect", "Connect Exigo, InfoTrax, ByDesign, or Pillars: credentials, data mappings, and sync settings", registerConnectCommands));
1598
1629
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["setPath"],"sources":["../src/providers.ts","../src/api.ts","../src/settings.ts","../src/summary.ts","../src/unmapped.ts","../src/walk.ts","../src/index.ts"],"sourcesContent":["/**\n * The back-office platforms Fluid Connect links to Fluid.\n *\n * Only what the API doesn't report lives here: the slug fluid-integrations\n * names each provider by, the droplet name the Fluid marketplace lists it\n * under, and which credential groups its connection needs. Settings,\n * supported mappings, and mapping options are read live from the API.\n */\n/** One credential the connection needs. `key` is the dotted path in\n * `auth_settings` (the same names `fluid connect credentials --set` takes). */\nexport interface CredentialField {\n key: string;\n label: string;\n /** Typed without echo, and never shown back. */\n secret?: boolean;\n /** Fluid has a default for it, so it can be left blank. */\n optional?: boolean;\n}\n\nconst MSSQL_CREDENTIALS: readonly CredentialField[] = [\n { key: \"mssql.dataserver\", label: \"SQL Server host (data server)\" },\n { key: \"mssql.database\", label: \"SQL Server database name\" },\n { key: \"mssql.username\", label: \"SQL Server login username\" },\n { key: \"mssql.password\", label: \"SQL Server login password\", secret: true },\n];\n\nexport const PROVIDERS = {\n exigo: {\n label: \"Exigo\",\n slug: \"exigo\",\n dropletName: \"Exigo\",\n // Reads changes from Exigo's SQL Server; writes through Exigo's API.\n credentialGroups: [\"api\", \"mssql\"],\n credentials: [\n { key: \"api.company\", label: \"Exigo company login (company code)\" },\n { key: \"api.username\", label: \"Exigo API service-account username\" },\n { key: \"api.password\", label: \"Exigo API service-account password\", secret: true },\n ...MSSQL_CREDENTIALS,\n ],\n // Exigo also asks `environment`, and `sandbox_id` outside production.\n environments: true,\n // Exigo's own values for the target side of a mapping.\n targetOptions: true,\n },\n bydesign: {\n label: \"ByDesign\",\n slug: \"bydesign\",\n dropletName: \"ByDesign\",\n credentialGroups: [\"api\"],\n credentials: [\n { key: \"api.company\", label: \"ByDesign tenant (company) code\" },\n { key: \"api.username\", label: \"ByDesign API service-account username\" },\n { key: \"api.password\", label: \"ByDesign API service-account password\", secret: true },\n { key: \"api.api_url\", label: \"Freedom API host (blank for Fluid's default)\", optional: true },\n ],\n environments: false,\n targetOptions: false,\n },\n infotrax: {\n label: \"InfoTrax\",\n slug: \"infotrax\",\n dropletName: \"Infotrax\",\n credentialGroups: [\"api\", \"mssql\"],\n credentials: MSSQL_CREDENTIALS,\n environments: false,\n targetOptions: false,\n },\n pillars: {\n label: \"Pillars\",\n slug: \"pillars\",\n dropletName: \"Pillars\",\n credentialGroups: [\"api\"],\n credentials: [\n { key: \"api.token\", label: \"Pillars API token\", secret: true },\n { key: \"api.api_url\", label: \"Pillars API base URL\" },\n ],\n environments: false,\n targetOptions: false,\n },\n} as const satisfies Record<\n string,\n {\n label: string;\n slug: string;\n dropletName: string;\n credentialGroups: readonly string[];\n credentials: readonly CredentialField[];\n environments: boolean;\n targetOptions: boolean;\n }\n>;\n\nexport type ProviderId = keyof typeof PROVIDERS;\nexport type Provider = (typeof PROVIDERS)[ProviderId];\n\nexport const PROVIDER_IDS = Object.keys(PROVIDERS) as ProviderId[];\n\n/** Scopes the Connect droplet is installed with (the same set Mist uses). */\nexport const CONNECT_DROPLET_SCOPES = [\n \"main\",\n \"members\",\n \"prospects\",\n \"products\",\n \"orders\",\n \"users\",\n \"settings\",\n] as const;\n\nexport class ConnectError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConnectError\";\n }\n}\n\nexport function providerOf(id: string): Provider & { id: ProviderId } {\n const key = id.trim().toLowerCase();\n if (!(key in PROVIDERS)) {\n throw new ConnectError(\n `Unknown provider \"${id}\". Use one of: ${PROVIDER_IDS.join(\", \")}.`,\n );\n }\n return { id: key as ProviderId, ...PROVIDERS[key as ProviderId] };\n}\n","import {\n CONNECT_DROPLET_SCOPES,\n ConnectError,\n type Provider,\n type ProviderId,\n} from \"./providers.js\";\n\n/** The slice of the CLI's API client these calls use. */\nexport interface ConnectClient {\n get<T>(endpoint: string, params?: Record<string, unknown>): Promise<T>;\n post<T>(endpoint: string, body?: unknown): Promise<T>;\n put<T>(endpoint: string, body?: unknown): Promise<T>;\n delete<T>(endpoint: string): Promise<T>;\n}\n\nexport const DEFAULT_INTEGRATIONS_BASE =\n \"https://fluid-integrations-106074092699.us-west3.run.app\";\n\n/** fluid-integrations, where Connect's configuration lives. It accepts the\n * same Fluid API token the CLI sends to api.fluid.app. */\nexport function integrationsBase(): string {\n return (process.env[\"FLUID_INTEGRATIONS_BASE\"] ?? DEFAULT_INTEGRATIONS_BASE)\n .trim()\n .replace(/\\/$/, \"\");\n}\n\ninterface IntegrationWire {\n id?: number;\n name?: string;\n droplet_uuid?: string | null;\n company_integration_id?: number | null;\n is_connected?: boolean | null;\n supported_entities?: string[];\n supported_sync_modes?: string[];\n}\n\nexport interface ProviderStatus {\n provider: ProviderId;\n label: string;\n /** False when the company has no connection to the provider yet. */\n installed: boolean;\n connected: boolean;\n companyIntegrationId: number | null;\n /** The official droplet for the provider, as fluid-integrations names it. */\n dropletUuid: string | null;\n /** fluid-integrations' id for the provider, used to create a connection. */\n integrationId: number | null;\n}\n\n/** The provider's integration as fluid-integrations sees this company. */\nexport async function providerStatus(\n integrations: ConnectClient,\n provider: Provider & { id: ProviderId },\n): Promise<ProviderStatus> {\n let wire: IntegrationWire;\n try {\n wire = await integrations.get<IntegrationWire>(\n `/api/v1/integrations/${provider.slug}`,\n );\n } catch (error) {\n if ((error as { status?: number }).status === 404) {\n throw new ConnectError(\n `${provider.label} isn't available on fluid-integrations for this company.`,\n );\n }\n throw error;\n }\n const ciId = wire.company_integration_id ?? null;\n return {\n provider: provider.id,\n label: provider.label,\n installed: ciId !== null,\n connected: wire.is_connected === true,\n companyIntegrationId: ciId,\n dropletUuid: wire.droplet_uuid ?? null,\n integrationId: wire.id ?? null,\n };\n}\n\n/** The company integration id, or a ConnectError saying to install first. */\nexport async function companyIntegrationId(\n integrations: ConnectClient,\n provider: Provider & { id: ProviderId },\n): Promise<number> {\n const status = await providerStatus(integrations, provider);\n if (status.companyIntegrationId === null) {\n throw new ConnectError(\n `${provider.label} isn't installed for this company. Run \\`fluid connect install ${provider.id} --yes\\` first.`,\n );\n }\n return status.companyIntegrationId;\n}\n\nexport interface CompanyIntegrationWire {\n id: number;\n name?: string;\n initialized?: boolean;\n environment?: string | null;\n sync_settings?: Record<string, unknown>;\n settings_schema?: Record<string, Record<string, SchemaSetting>>;\n supported_mappings?: string[];\n live_sync_state?: string | null;\n live_sync_degraded_reason?: string | null;\n}\n\nexport interface SchemaSetting {\n type?: string;\n value?: unknown;\n label?: string;\n description?: string;\n options?: Array<{ value: unknown; label?: string }>;\n multiple?: boolean;\n depends_on?: string | string[];\n}\n\nexport async function getCompanyIntegration(\n integrations: ConnectClient,\n ciId: number,\n): Promise<CompanyIntegrationWire> {\n return integrations.get<CompanyIntegrationWire>(\n `/api/v1/company_integrations/${ciId}`,\n );\n}\n\n/** Save credentials. fluid-integrations tests the connection with them and\n * answers 422 with the reason when it fails, so nothing broken is saved. */\nexport async function setCredentials(\n integrations: ConnectClient,\n ciId: number,\n authSettings: Record<string, unknown>,\n): Promise<{ saved: true }> {\n await integrations.put(`/api/v1/company_integrations/${ciId}`, {\n company_integration: { auth_settings: authSettings },\n });\n return { saved: true };\n}\n\n/**\n * Save credentials, creating the connection when the droplet is installed\n * but its install hasn't created the connection yet (the same request\n * Fluid's admin makes). Without an installed droplet nothing is created, so\n * `install` still installs it. Either way fluid-integrations tests the\n * connection first and answers 422 with the reason when it fails, so\n * nothing broken is saved.\n */\nexport async function saveCredentials(\n integrations: ConnectClient,\n fluid: ConnectClient,\n provider: Provider & { id: ProviderId },\n authSettings: Record<string, unknown>,\n): Promise<{ saved: true; created: boolean; companyIntegrationId: number }> {\n const status = await providerStatus(integrations, provider);\n if (status.companyIntegrationId !== null) {\n await setCredentials(integrations, status.companyIntegrationId, authSettings);\n return { saved: true, created: false, companyIntegrationId: status.companyIntegrationId };\n }\n // The droplet `install` would have installed: the one fluid-integrations\n // names, else the catalog's unambiguous name match.\n const dropletUuid =\n status.dropletUuid ?? (await findDropletUuid(fluid, provider).catch(() => null));\n if (\n status.integrationId === null ||\n dropletUuid === null ||\n !(await dropletInstalled(fluid, dropletUuid))\n ) {\n throw new ConnectError(\n `${provider.label} isn't installed for this company. Run \\`fluid connect install ${provider.id} --yes\\` first.`,\n );\n }\n const created = await integrations.post<{ id?: number; data?: { id?: number } }>(\n \"/api/v1/company_integrations\",\n {\n company_integration: {\n integration_id: status.integrationId,\n name: provider.label,\n droplet_uuid: dropletUuid,\n auth_settings: authSettings,\n },\n },\n );\n const id =\n created?.id ??\n created?.data?.id ??\n (await providerStatus(integrations, provider)).companyIntegrationId;\n if (typeof id !== \"number\") {\n throw new ConnectError(`${provider.label} accepted the credentials, but the connection's id didn't come back. Run \\`fluid connect show ${provider.id}\\`.`);\n }\n return { saved: true, created: true, companyIntegrationId: id };\n}\n\n/** Merge a sync-settings patch into the saved settings. */\nexport async function updateSyncSettings(\n integrations: ConnectClient,\n ciId: number,\n patch: Record<string, unknown>,\n): Promise<CompanyIntegrationWire> {\n await integrations.put(`/api/v1/company_integrations/${ciId}`, {\n company_integration: { sync_settings: patch },\n });\n return getCompanyIntegration(integrations, ciId);\n}\n\n/** A mapping type as its route names it, e.g. `customer_type`. */\nexport function mappingPath(ciId: number, type: string): string {\n if (!/^[a-z_]+$/.test(type)) {\n throw new ConnectError(\n `\"${type}\" isn't a mapping type. Use names like customer_type or warehouse (see \\`fluid connect show\\`).`,\n );\n }\n return `/api/v1/company_integrations/${ciId}/${type}_mappings`;\n}\n\nexport async function listMappings(\n integrations: ConnectClient,\n ciId: number,\n type: string,\n): Promise<unknown> {\n return integrations.get(mappingPath(ciId, type));\n}\n\n/** Values to choose from: Fluid's for the source side, and for Exigo also\n * Exigo's own for the target side. */\nexport async function mappingOptions(\n integrations: ConnectClient,\n ciId: number,\n type: string,\n provider: Provider,\n): Promise<{ fluid: unknown; provider?: unknown }> {\n // Some types (ship methods) list only Exigo's values, so a missing\n // source_options leaves `fluid` null rather than failing the command.\n let fluid: unknown = null;\n try {\n fluid = await integrations.get(`${mappingPath(ciId, type)}/source_options`);\n } catch (error) {\n if ((error as { status?: number }).status !== 404) throw error;\n if (!provider.targetOptions) throw error;\n }\n if (!provider.targetOptions) return { fluid };\n return {\n fluid,\n provider: await integrations.get(\n `${mappingPath(ciId, type)}/exigo_options`,\n ),\n };\n}\n\n/** Create one mapping row. `fields` are the row's attributes, wrapped in\n * the singular resource key the API expects (`customer_type_mapping`). */\nexport async function createMapping(\n integrations: ConnectClient,\n ciId: number,\n type: string,\n fields: Record<string, unknown>,\n): Promise<unknown> {\n return integrations.post(mappingPath(ciId, type), {\n [`${type}_mapping`]: fields,\n });\n}\n\nexport async function deleteMapping(\n integrations: ConnectClient,\n ciId: number,\n type: string,\n id: number,\n): Promise<{ deleted: true; id: number }> {\n await integrations.delete(`${mappingPath(ciId, type)}/${id}`);\n return { deleted: true, id };\n}\n\ninterface InstallationListWire {\n droplet_installations?: Array<{ droplet_uuid?: string; active?: boolean }>;\n meta?: { pagination?: { total_pages?: number } };\n}\n\n/** Whether the company has the droplet installed and active in Fluid (one\n * pending uninstall is listed, but inactive). */\nexport async function dropletInstalled(\n fluid: ConnectClient,\n dropletUuid: string,\n): Promise<boolean> {\n for (let page = 1; page <= CATALOG_PAGES; page += 1) {\n const data = await fluid.get<InstallationListWire>(\"/api/droplet_installations\", {\n page,\n per_page: 100,\n });\n const rows = data.droplet_installations ?? [];\n if (rows.some((row) => row.droplet_uuid === dropletUuid && row.active !== false)) return true;\n const totalPages = data.meta?.pagination?.total_pages ?? 1;\n if (page >= totalPages || rows.length < 100) break;\n }\n return false;\n}\n\ninterface DropletListWire {\n droplets?: Array<{ uuid?: string; name?: string }>;\n meta?: { pagination?: { total_pages?: number } };\n}\n\nconst CATALOG_PAGES = 10;\n\n/** Install the provider's droplet in Fluid, which creates its company\n * integration on fluid-integrations. */\nexport async function installProvider(\n fluid: ConnectClient,\n provider: Provider,\n /** The official droplet from fluid-integrations, when it names one. */\n officialUuid: string | null = null,\n): Promise<{ installed: true; dropletUuid: string }> {\n const dropletUuid = officialUuid ?? (await findDropletUuid(fluid, provider));\n await fluid.post(\"/api/droplet_installations\", {\n droplet_uuid: dropletUuid,\n scopes: CONNECT_DROPLET_SCOPES,\n });\n return { installed: true, dropletUuid };\n}\n\nasync function findDropletUuid(\n fluid: ConnectClient,\n provider: Provider,\n): Promise<string> {\n const wanted = provider.dropletName.toLowerCase();\n const matches = new Set<string>();\n for (let page = 1; page <= CATALOG_PAGES; page += 1) {\n const data = await fluid.get<DropletListWire>(\"/api/droplets\", {\n page,\n per_page: 100,\n });\n for (const droplet of data.droplets ?? []) {\n if (droplet.uuid && droplet.name?.trim().toLowerCase() === wanted) matches.add(droplet.uuid);\n }\n const totalPages = data.meta?.pagination?.total_pages ?? 1;\n if (page >= totalPages || (data.droplets ?? []).length < 100) break;\n }\n // A company's own droplet can share the name, so a name alone must be\n // unambiguous before it's installed with Connect's scopes.\n if (matches.size === 1) return [...matches][0]!;\n if (matches.size > 1) {\n throw new ConnectError(\n `More than one droplet is named ${provider.dropletName}, and fluid-integrations didn't say which is ${provider.label}'s. Ask Fluid to check the ${provider.label} integration's droplet.`,\n );\n }\n throw new ConnectError(\n `The ${provider.label} droplet isn't in this company's droplet catalog. Ask Fluid to make it available.`,\n );\n}\n","import { ConnectError } from \"./providers.js\";\n\n/**\n * Parse `--set` values into the nested object the API takes.\n *\n * `api.username=alice` → `{ api: { username: \"alice\" } }`. Values that read\n * as JSON (true, 42, [\"a\",\"b\"], {\"x\":1}) are parsed; anything else stays a\n * string, so a password like `abc123` is never mangled.\n */\nexport function parseAssignments(\n assignments: readonly string[],\n /** Keep every value a string, as credentials need: a password like\n * `123456` or `true` must reach the API exactly as typed. */\n opts: { strings?: boolean } = {},\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const assignment of assignments) {\n const at = assignment.indexOf(\"=\");\n if (at <= 0) {\n throw new ConnectError(\n `\"${assignment}\" isn't key=value. Use dots for nesting, e.g. api.username=alice.`,\n );\n }\n const path = assignment.slice(0, at).split(\".\");\n if (path.some((part) => part.length === 0)) {\n throw new ConnectError(`\"${assignment}\" has an empty key segment.`);\n }\n if (path.some((part) => UNSAFE_KEYS.has(part))) {\n throw new ConnectError(`\"${assignment}\" uses a reserved key name.`);\n }\n const raw = assignment.slice(at + 1);\n setPath(out, path, opts.strings ? raw : parseValue(raw));\n }\n return out;\n}\n\n// Keys that would reach Object.prototype through a plain object.\nconst UNSAFE_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nfunction parseValue(raw: string): unknown {\n if (/^(true|false|null|-?\\d+(\\.\\d+)?)$/.test(raw) || /^[[{]/.test(raw)) {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n return raw;\n}\n\nfunction setPath(\n target: Record<string, unknown>,\n path: string[],\n value: unknown,\n): void {\n let node = target;\n for (const key of path.slice(0, -1)) {\n const next = node[key];\n if (typeof next !== \"object\" || next === null || Array.isArray(next)) {\n node[key] = {};\n }\n node = node[key] as Record<string, unknown>;\n }\n node[path[path.length - 1]!] = value;\n}\n\n/**\n * Sync settings are saved per entity as the schema entry itself, with the\n * chosen value under `value`: `entities.<entity>.settings.<key>.value`.\n * `--set` takes the short `<entity>.<key>=value` form `fluid connect\n * settings` prints, and this expands it. Exigo's `sponsor_tree` is the one\n * top-level setting (`sync_settings.sponsor_tree.value`); write it as\n * `sponsor_tree.sponsor_tree=value`.\n */\nexport function syncSettingsPatch(\n assignments: readonly string[],\n): Record<string, unknown> {\n const entries: Array<[string, unknown]> = [];\n for (const assignment of assignments) {\n const parsed = parseAssignments([assignment]);\n const [group, values] = Object.entries(parsed)[0]!;\n if (typeof values !== \"object\" || values === null || Array.isArray(values)) {\n throw new ConnectError(\n `\"${assignment}\" needs an entity and a setting, e.g. order.refund_handling=hold.`,\n );\n }\n for (const [key, value] of Object.entries(values)) {\n entries.push([`${group}.${key}`, value]);\n }\n }\n return patchFromEntries(entries);\n}\n\n/**\n * A patch that puts settings back to their schema defaults: all of them,\n * or only the `<entity>.<key>` names in `only`. Unknown names are refused\n * rather than silently skipped.\n */\nexport function defaultsPatch(\n schema: Record<string, Record<string, { value?: unknown }>>,\n only: readonly string[] = [],\n): Record<string, unknown> {\n const all = Object.entries(schema).flatMap(([entity, settings]) =>\n Object.entries(settings).map(\n ([key, setting]) => [`${entity}.${key}`, setting.value] as [string, unknown],\n ),\n );\n // A setting whose schema has no default is left as it is: there's\n // nothing to restore, and sending null would wipe a saved value.\n const withDefault = all.filter(([, value]) => value !== undefined);\n if (only.length === 0) return patchFromEntries(withDefault);\n const known = new Map(all);\n const unknown = only.filter((key) => !known.has(key));\n if (unknown.length > 0) {\n throw new ConnectError(\n `Unknown setting(s): ${unknown.join(\", \")}. List them with \\`fluid connect settings <provider>\\`.`,\n );\n }\n const noDefault = only.filter((key) => known.get(key) === undefined);\n if (noDefault.length > 0) {\n throw new ConnectError(`No default to restore for: ${noDefault.join(\", \")}.`);\n }\n return patchFromEntries(only.map((key) => [key, known.get(key)]));\n}\n\n/** Settings worth deciding before the first sync, in the order the setup\n * guides list them. A provider without one of them skips it. */\nexport const COMMON_SETTINGS = [\n \"customer.duplicate_customer_handling\",\n \"customer.unverified_customer_handling\",\n \"customer.customer_creation_sync\",\n \"customer.customer_changelog_sync\",\n \"order.refund_handling\",\n \"order.duplicate_order_handling\",\n \"order.order_creation_sync\",\n \"order.order_changelog_sync\",\n \"product.new_inactive\",\n \"product.warehouseless_product_handling\",\n \"rank.sync_ranks\",\n \"rank.pulled_from\",\n \"sponsor_tree.sponsor_tree\",\n \"customer.fetch_mode\",\n \"order_payment.skip_payment_on_export\",\n] as const;\n\n/** A patch setting `<entity>.<key>` names to values exactly as given (no\n * parsing), in the shape `--set` and `--defaults` send. */\nexport function settingsPatch(\n values: Record<string, unknown>,\n): Record<string, unknown> {\n return patchFromEntries(Object.entries(values));\n}\n\nfunction patchFromEntries(\n entries: ReadonlyArray<[string, unknown]>,\n): Record<string, unknown> {\n const entities: Record<string, { settings: Record<string, unknown> }> = {};\n const patch: Record<string, unknown> = {};\n for (const [name, value] of entries) {\n const at = name.indexOf(\".\");\n const group = name.slice(0, at);\n const key = name.slice(at + 1);\n if (group === \"sponsor_tree\" && key === \"sponsor_tree\") {\n patch[\"sponsor_tree\"] = { value };\n continue;\n }\n entities[group] ??= { settings: {} };\n entities[group].settings[key] = { value };\n }\n if (Object.keys(entities).length > 0) patch[\"entities\"] = entities;\n return patch;\n}\n","import type { CompanyIntegrationWire, SchemaSetting } from \"./api.js\";\n\nexport interface SettingSummary {\n /** Pass to `fluid connect settings --set <key>=<value>`. */\n key: string;\n label: string | null;\n description: string | null;\n type: string | null;\n value: unknown;\n default: unknown;\n options?: unknown[];\n multiple?: boolean;\n dependsOn?: string | string[];\n}\n\n/**\n * Every sync setting the provider supports, with the value saved for this\n * company next to the default. Saved values live at\n * `entities.<entity>.settings.<key>.value` (Exigo's `sponsor_tree` at\n * `sponsor_tree.value`); a setting never saved reads as its default.\n */\nexport function summarizeSettings(\n ci: CompanyIntegrationWire,\n): SettingSummary[] {\n const schema = ci.settings_schema ?? {};\n const saved = ci.sync_settings ?? {};\n const out: SettingSummary[] = [];\n for (const [entity, settings] of Object.entries(schema)) {\n for (const [name, setting] of Object.entries(settings)) {\n out.push(summarize(entity, name, setting, savedValue(saved, entity, name)));\n }\n }\n return out;\n}\n\nfunction summarize(\n entity: string,\n name: string,\n setting: SchemaSetting,\n saved: { found: boolean; value: unknown },\n): SettingSummary {\n return {\n key: `${entity}.${name}`,\n label: setting.label ?? null,\n description: setting.description ?? null,\n type: setting.type ?? null,\n value: saved.found ? saved.value : (setting.value ?? null),\n default: setting.value ?? null,\n ...(setting.options ? { options: setting.options.map((o) => o.value) } : {}),\n ...(setting.multiple !== undefined ? { multiple: setting.multiple } : {}),\n ...(setting.depends_on ? { dependsOn: setting.depends_on } : {}),\n };\n}\n\nfunction savedValue(\n saved: Record<string, unknown>,\n entity: string,\n name: string,\n): { found: boolean; value: unknown } {\n const entry =\n entity === \"sponsor_tree\" && name === \"sponsor_tree\"\n ? saved[\"sponsor_tree\"]\n : dig(saved, [\"entities\", entity, \"settings\", name]);\n if (entry && typeof entry === \"object\" && \"value\" in entry) {\n return { found: true, value: (entry as { value: unknown }).value };\n }\n return { found: false, value: undefined };\n}\n\nfunction dig(node: unknown, path: string[]): unknown {\n let current = node;\n for (const key of path) {\n if (!current || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[key];\n }\n return current;\n}\n","import {\n createMapping,\n getCompanyIntegration,\n mappingPath,\n type ConnectClient,\n} from \"./api.js\";\nimport type { Provider } from \"./providers.js\";\n\n/** The field each mapping type keeps the Fluid (source) value in. A row\n * whose source is `*` is the fallback for every value without its own row. */\nexport const SOURCE_FIELD: Record<string, string> = {\n customer_type: \"source_type_slug\",\n customer_status: \"source_status_slug\",\n order_status: \"source_status_slug\",\n order_type: \"source_type_slug\",\n payable_type: \"source_type_slug\",\n price_type: \"source_price_type_title\",\n warehouse: \"source_warehouse_title\",\n customer_warehouse: \"source_country_code\",\n payment_type: \"source_payment_method\",\n wallet_type: \"source_payment_method\",\n fraud_decision: \"source_decision\",\n ship_method: \"source_shipping_method_title\",\n product_type: \"source_title\",\n country_market: \"source_title\",\n tree: \"source_title\",\n rank: \"source_title\",\n};\n\n/** The field each mapping type keeps the provider's (target) value in. */\nexport const TARGET_FIELD: Record<string, string> = {\n customer_type: \"target_type_id\",\n customer_status: \"target_status_id\",\n order_status: \"target_status_id\",\n order_type: \"target_type_id\",\n payable_type: \"target_payable_id\",\n price_type: \"target_price_type_id\",\n warehouse: \"external_warehouse_id\",\n customer_warehouse: \"target_warehouse_id\",\n payment_type: \"target_payment_type\",\n wallet_type: \"target_wallet_type_id\",\n fraud_decision: \"target_field_value\",\n ship_method: \"exigo_ship_method_id\",\n product_type: \"target_title\",\n country_market: \"target_title\",\n tree: \"target_title\",\n rank: \"target_title\",\n};\n\n/** Types whose Fluid values the API lists (`source_options`), with a\n * `mapped` flag on each. Ranks and ship methods have no such list. */\nconst LISTS_FLUID_VALUES = new Set([\n \"customer_type\",\n \"customer_status\",\n \"order_status\",\n \"order_type\",\n \"payable_type\",\n \"price_type\",\n \"warehouse\",\n \"customer_warehouse\",\n \"payment_type\",\n \"wallet_type\",\n \"fraud_decision\",\n \"currency_point_account\",\n \"product_type\",\n \"country_market\",\n \"tree\",\n]);\n\ninterface FluidOption {\n id?: string | number;\n title?: string;\n slug?: string;\n mapped?: boolean;\n}\n\ninterface ProviderOption {\n id?: string | number;\n title?: string;\n}\n\n/** How to build a row from a Fluid option and the Exigo option with the\n * same title. Only types whose target is an Exigo id are listed: a title\n * match then names exactly one Exigo record. */\nconst AUTO_MAP: Record<\n string,\n (fluid: FluidOption, provider: ProviderOption) => Record<string, unknown> | null\n> = {\n customer_type: (f, p) => f.slug ? { source_type_slug: f.slug, target_type_id: String(p.id) } : null,\n customer_status: (f, p) => f.slug ? { source_status_slug: f.slug, target_status_id: String(p.id) } : null,\n order_status: (f, p) => f.slug ? { source_status_slug: f.slug, target_status_id: String(p.id) } : null,\n order_type: (f, p) => f.slug ? { source_type_slug: f.slug, target_type_id: String(p.id) } : null,\n payable_type: (f, p) => f.slug ? { source_type_slug: f.slug, target_payable_id: String(p.id) } : null,\n price_type: (f, p) => f.title ? { source_price_type_title: f.title, target_price_type_id: String(p.id) } : null,\n warehouse: (f, p) =>\n f.title && f.id !== undefined\n ? { fluid_warehouse_id: f.id, source_warehouse_title: f.title, external_warehouse_id: String(p.id) }\n : null,\n};\n\nexport interface UnmappedValue {\n /** What to put in the source field when you add the row. */\n value: string;\n title: string | null;\n /** The Fluid record's id, which a warehouse row also needs. */\n id?: string | number;\n}\n\nexport interface MappingTypeReport {\n type: string;\n /** covered: every Fluid value has a row or a `*` fallback covers them.\n * needs_mapping: `unmapped` lists the values a person must map.\n * check_by_hand: the API doesn't list Fluid's values for this type.\n * error: the values couldn't be read; see `error`. */\n status: \"covered\" | \"needs_mapping\" | \"check_by_hand\" | \"error\";\n sourceField: string | null;\n fallback?: boolean;\n unmapped?: UnmappedValue[];\n autoMapped?: Array<Record<string, unknown>>;\n error?: string;\n}\n\nconst norm = (text: string | undefined): string =>\n (text ?? \"\").trim().toLowerCase();\n\n/** The value a row's source field takes for a Fluid option: the title for\n * fields keyed by title (`source_price_type_title`), else the slug. */\nexport function sourceValue(option: FluidOption, sourceField?: string | null): string {\n const value = sourceField?.endsWith(\"_title\")\n ? (option.title ?? option.slug ?? option.id)\n : (option.slug ?? option.title ?? option.id);\n return String(value ?? \"\");\n}\n\n/** Pair unmapped Fluid options with provider options whose title matches\n * exactly (ignoring case and edge spaces). A title that more than one\n * provider option shares is ambiguous and left for a person. */\nexport function exactTitleMatches(\n unmapped: FluidOption[],\n providerOptions: ProviderOption[],\n): Array<{ fluid: FluidOption; provider: ProviderOption }> {\n const byTitle = new Map<string, ProviderOption[]>();\n for (const option of providerOptions) {\n const key = norm(option.title);\n if (!key || option.id === undefined || option.id === null) continue;\n byTitle.set(key, [...(byTitle.get(key) ?? []), option]);\n }\n const pairs: Array<{ fluid: FluidOption; provider: ProviderOption }> = [];\n for (const fluid of unmapped) {\n const candidates = byTitle.get(norm(fluid.title)) ?? [];\n if (candidates.length === 1) pairs.push({ fluid, provider: candidates[0]! });\n }\n return pairs;\n}\n\nfunction optionsOf(response: unknown): unknown[] {\n const options = (response as { options?: unknown })?.options;\n return Array.isArray(options) ? options : [];\n}\n\nfunction rowsOf(response: unknown, type: string): Array<Record<string, unknown>> {\n const rows = (response as Record<string, unknown> | null)?.[`${type}_mappings`];\n return Array.isArray(rows) ? (rows as Array<Record<string, unknown>>) : [];\n}\n\nfunction errorText(error: unknown): string {\n const body = (error as { body?: Record<string, unknown> | null }).body;\n const detail = body?.[\"error\"] ?? body?.[\"errors\"];\n if (typeof detail === \"string\") return detail;\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * For every mapping type the provider supports, the Fluid values that still\n * need a row. With `autoMap`, rows are first created for values whose title\n * matches exactly one provider value (Exigo only, and only types whose target\n * is an id), so what's returned is what a person still has to decide.\n */\nexport async function unmappedReport(\n integrations: ConnectClient,\n ciId: number,\n provider: Provider,\n autoMap: boolean,\n): Promise<MappingTypeReport[]> {\n const ci = await getCompanyIntegration(integrations, ciId);\n const types = (ci.supported_mappings ?? [])\n .map((card) => card.replace(/_title$/, \"\"))\n .filter((type) => type in SOURCE_FIELD || LISTS_FLUID_VALUES.has(type));\n\n const reports: MappingTypeReport[] = [];\n for (const type of types) {\n reports.push(await reportFor(integrations, ciId, provider, type, autoMap));\n }\n return reports;\n}\n\nasync function reportFor(\n integrations: ConnectClient,\n ciId: number,\n provider: Provider,\n type: string,\n autoMap: boolean,\n): Promise<MappingTypeReport> {\n const sourceField = SOURCE_FIELD[type] ?? null;\n if (!LISTS_FLUID_VALUES.has(type)) {\n return { type, status: \"check_by_hand\", sourceField };\n }\n // Kept outside the try so a later failure still reports what was saved.\n const autoMapped: Array<Record<string, unknown>> = [];\n try {\n const path = mappingPath(ciId, type);\n const [optionsResponse, rowsResponse] = await Promise.all([\n integrations.get(`${path}/source_options`),\n integrations.get(path),\n ]);\n const fallback =\n sourceField !== null &&\n rowsOf(rowsResponse, type).some((row) => row[sourceField] === \"*\");\n if (fallback) return { type, status: \"covered\", sourceField, fallback: true };\n\n let unmapped = (optionsOf(optionsResponse) as FluidOption[]).filter(\n (option) => option.mapped === false,\n );\n const build = AUTO_MAP[type];\n if (autoMap && provider.targetOptions && build && unmapped.length > 0) {\n const providerOptions = optionsOf(\n await integrations.get(`${path}/exigo_options`),\n ) as ProviderOption[];\n for (const { fluid, provider: match } of exactTitleMatches(unmapped, providerOptions)) {\n const fields = build(fluid, match);\n if (!fields) continue;\n await createMapping(integrations, ciId, type, fields);\n autoMapped.push(fields);\n unmapped = unmapped.filter((option) => option !== fluid);\n }\n }\n return {\n type,\n status: unmapped.length === 0 ? \"covered\" : \"needs_mapping\",\n sourceField,\n unmapped: unmapped.map((option) => ({\n value: sourceValue(option, sourceField),\n title: option.title ?? null,\n ...(option.id !== undefined && option.id !== null ? { id: option.id } : {}),\n })),\n ...(autoMapped.length > 0 ? { autoMapped } : {}),\n };\n } catch (error) {\n return {\n type,\n status: \"error\",\n sourceField,\n error: errorText(error),\n ...(autoMapped.length > 0 ? { autoMapped } : {}),\n };\n }\n}\n","// `fluid connect walk <provider>`: the setup guide as a conversation. It\n// installs the provider if needed, asks for the credentials, maps each\n// unmapped value, and goes through the common sync settings. Every answer\n// is saved as it's given, through the same functions the non-interactive\n// commands use, and a person can go back, skip, or open a menu to jump to a\n// part or change an answer.\n\nimport {\n NAV_HELP,\n type Choice,\n type Nav,\n type Prompter,\n type Question,\n} from \"@fluid-app/fluid-cli\";\n\nimport {\n createMapping,\n deleteMapping,\n getCompanyIntegration,\n installProvider,\n mappingPath,\n providerStatus,\n saveCredentials,\n updateSyncSettings,\n type CompanyIntegrationWire,\n type ConnectClient,\n type SchemaSetting,\n} from \"./api.js\";\nimport {\n ConnectError,\n type CredentialField,\n type Provider,\n type ProviderId,\n} from \"./providers.js\";\nimport { COMMON_SETTINGS, defaultsPatch, settingsPatch } from \"./settings.js\";\nimport { summarizeSettings } from \"./summary.js\";\nimport {\n SOURCE_FIELD,\n TARGET_FIELD,\n unmappedReport,\n type MappingTypeReport,\n type UnmappedValue,\n} from \"./unmapped.js\";\n\nexport interface WalkDeps {\n /** The Fluid API, for installing the droplet. */\n fluid: ConnectClient;\n /** fluid-integrations, where the connection lives. */\n integrations: ConnectClient;\n provider: Provider & { id: ProviderId };\n prompter: Prompter;\n /** Waits between checks for a just-installed connection. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface WalkResult {\n provider: ProviderId;\n installed: boolean;\n connected: boolean;\n /** True when the walk saved credentials that passed the connection test. */\n credentialsSaved: boolean;\n /** Rows added by matching names (Exigo). */\n autoMapped: number;\n /** Rows added from answers, including `*` fallbacks. */\n rowsAdded: number;\n /** Every mapping type is covered. Null when it couldn't be checked. */\n mappingsDone: boolean | null;\n /** Types that still have values without a row, with how many. */\n stillUnmapped: Array<{ type: string; count: number | null; status: string }>;\n settingsChanged: string[];\n settingsRestored: boolean;\n /** The questions passed over with `>`. */\n skipped: string[];\n /** Stopped before the end (Ctrl-C or Stop in the menu). */\n stopped: boolean;\n}\n\n// ---------------------------------------------------------------- steps\n\ntype Phase = \"credentials\" | \"mappings\" | \"settings\";\n\nconst PHASE_NAMES: Record<Phase, string> = {\n credentials: \"Credentials\",\n mappings: \"Mappings\",\n settings: \"Settings\",\n};\n\n/** What a step did: answered, asked to move, or wants to go elsewhere. */\ntype Outcome =\n | { done: true; answer?: string }\n | { nav: Nav }\n | { retry: true }\n | { goto: string }\n | { stop: true };\n\ninterface Step {\n id: string;\n phase: Phase;\n /** Steps generated together (one report, one settings pass) share a group\n * so they can be replaced as a whole. */\n group?: string;\n /** How the menu names it. */\n title: string;\n /** False for steps that only act (save, load), which back passes over. */\n asks: boolean;\n when?: () => boolean;\n run(canGoBack: boolean): Promise<Outcome>;\n}\n\nconst DONE: Outcome = { done: true };\nconst FALLBACK = \"*\";\n\n/** The reason a request failed: the provider's, on a failed connection test. */\nexport function reasonOf(error: unknown): string {\n const body = (error as { body?: Record<string, unknown> | null }).body;\n const detail = body?.[\"errors\"] ?? body?.[\"error\"] ?? body?.[\"error_message\"];\n if (typeof detail === \"string\") return detail;\n if (Array.isArray(detail)) return detail.map(String).join(\"; \");\n if (detail && typeof detail === \"object\") {\n return Object.entries(detail)\n .map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(\", \") : String(value)}`)\n .join(\"; \");\n }\n return error instanceof Error ? error.message : String(error);\n}\n\nconst words = (type: string) => type.replace(/_/g, \" \");\n\nfunction display(value: unknown): string {\n if (value === undefined || value === null || value === \"\") return \"(none)\";\n if (typeof value === \"boolean\") return value ? \"Yes\" : \"No\";\n return typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\nfunction setPath(target: Record<string, unknown>, key: string, value: string): void {\n const path = key.split(\".\");\n let node = target;\n for (const part of path.slice(0, -1)) {\n if (typeof node[part] !== \"object\" || node[part] === null) node[part] = {};\n node = node[part] as Record<string, unknown>;\n }\n node[path[path.length - 1]!] = value;\n}\n\nfunction rowIdOf(response: unknown, type: string): number | undefined {\n const record = response as Record<string, unknown> | null;\n const row = (record?.[`${type}_mapping`] ?? record) as { id?: unknown } | null;\n return typeof row?.id === \"number\" ? row.id : undefined;\n}\n\n// ----------------------------------------------------------------- walk\n\nexport async function walkConnect(deps: WalkDeps): Promise<WalkResult> {\n const { integrations, provider, prompter } = deps;\n const sleep = deps.sleep ?? ((ms: number) => new Promise((r) => setTimeout(r, ms)));\n const label = provider.label;\n\n const result: WalkResult = {\n provider: provider.id,\n installed: false,\n connected: false,\n credentialsSaved: false,\n autoMapped: 0,\n rowsAdded: 0,\n mappingsDone: null,\n stillUnmapped: [],\n settingsChanged: [],\n settingsRestored: false,\n skipped: [],\n stopped: false,\n };\n\n prompter.note(NAV_HELP);\n\n // ------------------------------------------------------------ install\n let status = await providerStatus(integrations, provider);\n if (!status.installed) {\n const choice = await prompter.menu(\n `${label} isn't installed for this company. Installing it adds the ${label} droplet in Fluid, which creates the connection (changes live config).`,\n [\n { title: `Install ${label}`, value: \"install\" },\n { title: \"Stop\", value: \"stop\" },\n ],\n );\n if (choice !== \"install\") {\n result.stopped = true;\n return result;\n }\n await installProvider(deps.fluid, provider, status.dropletUuid);\n prompter.note(`Installed ${label}. Waiting for the connection…`);\n for (let attempt = 0; attempt < 10 && !status.installed; attempt += 1) {\n await sleep(2000);\n status = await providerStatus(integrations, provider);\n }\n if (!status.installed) {\n prompter.note(`The connection isn't there yet, so saving the credentials will create it.`);\n }\n }\n result.installed = true;\n result.connected = status.connected;\n /** Null until the connection exists; saving credentials creates it. */\n let ciId: number | null = status.companyIntegrationId;\n let ci: CompanyIntegrationWire =\n ciId === null ? { id: 0 } : await getCompanyIntegration(integrations, ciId);\n /** The connection, for the parts that need one (guarded at mappings-start). */\n const connection = (): number => {\n if (ciId === null) throw new ConnectError(`${label} has no connection yet. Save its credentials first.`);\n return ciId;\n };\n\n // --------------------------------------------------------- the walk\n const steps: Step[] = [];\n const history: string[] = [];\n const skipped = new Map<string, string>();\n const answers = new Map<string, string>();\n let index = 0;\n /** The step reached by back or the menu: asked even if answered. */\n let targeted: string | undefined;\n\n const indexOf = (id: string) => steps.findIndex((s) => s.id === id);\n\n /** Put `fresh` in place of the group's steps, right after `afterId`. */\n const replaceGroup = (group: string, afterId: string, fresh: Step[]) => {\n for (let i = steps.length - 1; i >= 0; i -= 1) {\n if (steps[i]!.group === group) {\n answers.delete(steps[i]!.id);\n skipped.delete(steps[i]!.id);\n steps.splice(i, 1);\n }\n }\n steps.splice(indexOf(afterId) + 1, 0, ...fresh);\n };\n\n const ask = (question: Omit<Question, \"canGoBack\">, canGoBack: boolean) =>\n prompter.ask({ ...question, canGoBack });\n\n // ------------------------------------------------------ credentials\n const draft = new Map<string, string>();\n const credentialSteps: Step[] = [];\n const firstCredential = () => credentialSteps.find((s) => s.asks && s.id !== \"credentials-keep\")!.id;\n\n if (status.connected) {\n credentialSteps.push({\n id: \"credentials-keep\",\n phase: \"credentials\",\n title: \"Change the credentials?\",\n asks: true,\n run: async (canGoBack) => {\n const reply = await ask(\n {\n section: PHASE_NAMES.credentials,\n message: `${label} is connected. Change its credentials?`,\n kind: \"select\",\n choices: [\n { title: \"Keep the saved credentials\", value: \"keep\" },\n { title: \"Change them\", value: \"change\" },\n ],\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n if (reply.value === \"keep\") {\n answers.set(\"credentials-keep\", \"Keep\");\n history.push(\"credentials-keep\");\n return { goto: \"mappings-start\" };\n }\n return { done: true, answer: \"Change\" };\n },\n });\n }\n\n const required = (field: CredentialField) => !field.optional && !result.connected;\n\n const credentialStep = (field: CredentialField, when?: () => boolean): Step => ({\n id: `credential:${field.key}`,\n phase: \"credentials\",\n title: field.label,\n asks: true,\n ...(when ? { when } : {}),\n run: async (canGoBack) => {\n const typed = draft.get(field.key);\n const reply = await ask(\n {\n section: `${PHASE_NAMES.credentials} · ${field.key}`,\n message: field.label,\n kind: field.secret ? \"secret\" : \"text\",\n ...(typed !== undefined ? { current: field.secret ? \"••••••\" : typed } : {}),\n ...(result.connected\n ? { hint: \"Enter keeps the saved value\" }\n : field.optional\n ? { hint: \"optional; Enter leaves it blank\" }\n : {}),\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n const value = reply.value.trim();\n if (value === \"\") {\n if (typed !== undefined || !required(field)) return { done: true, answer: typed !== undefined ? \"(entered)\" : \"(unchanged)\" };\n prompter.note(`${label} needs this. Type it, or > to skip.`);\n return { retry: true };\n }\n draft.set(field.key, value);\n return { done: true, answer: field.secret ? \"(entered)\" : value };\n },\n });\n\n if (provider.environments) {\n credentialSteps.push({\n id: \"credential:environment\",\n phase: \"credentials\",\n title: `${label} environment`,\n asks: true,\n run: async (canGoBack) => {\n const reply = await ask(\n {\n section: `${PHASE_NAMES.credentials} · environment`,\n message: `Which ${label} environment?`,\n kind: \"select\",\n choices: [\n { title: \"Production (your live Exigo)\", value: \"production\" },\n { title: \"Sandbox\", value: \"sandbox\" },\n ],\n ...(draft.get(\"environment\") ?? ci.environment\n ? { current: draft.get(\"environment\") ?? ci.environment ?? undefined }\n : {}),\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n const environment = reply.value.trim() || draft.get(\"environment\") || ci.environment;\n if (!environment) {\n prompter.note(\"Choose an environment, or > to skip.\");\n return { retry: true };\n }\n draft.set(\"environment\", environment);\n return { done: true, answer: environment };\n },\n });\n credentialSteps.push(\n credentialStep(\n { key: \"sandbox_id\", label: \"Exigo sandbox ID (the number in sandboxapi<ID>.exigo.com)\" },\n () => (draft.get(\"environment\") ?? ci.environment) !== \"production\",\n ),\n );\n }\n for (const field of provider.credentials) credentialSteps.push(credentialStep(field));\n\n credentialSteps.push({\n id: \"credentials-save\",\n phase: \"credentials\",\n title: \"Save the credentials\",\n asks: false,\n run: async () => {\n if (draft.size === 0) return DONE;\n const authSettings: Record<string, unknown> = {};\n for (const [key, value] of draft) setPath(authSettings, key, value);\n prompter.note(`Testing the connection to ${label}…`);\n try {\n ciId = (await saveCredentials(integrations, deps.fluid, provider, authSettings)).companyIntegrationId;\n } catch (error) {\n prompter.note(`${label} didn't accept the credentials, so nothing was saved: ${reasonOf(error)}`);\n prompter.note(\"Check the values and answer again. Enter keeps what you typed.\");\n for (const step of credentialSteps) answers.delete(step.id);\n return { goto: firstCredential() };\n }\n draft.clear();\n result.credentialsSaved = true;\n result.connected = true;\n prompter.note(\"Connected. The credentials are saved.\");\n return DONE;\n },\n });\n steps.push(...credentialSteps);\n\n // --------------------------------------------------------- mappings\n let autoMap = false;\n const providerChoices = new Map<string, Choice[]>();\n /** Types a `*` row now covers, so their other values aren't asked. */\n const fallbackTypes = new Set<string>();\n /** The row each mapping answer created, replaced when it's changed. */\n const rowsByStep = new Map<string, { type: string; id?: number; fallback: boolean }>();\n\n steps.push({\n id: \"mappings-start\",\n phase: \"mappings\",\n title: \"Mappings\",\n asks: false,\n run: async () => {\n if (ciId !== null) return DONE;\n prompter.note(\n `Mappings and settings need the connection, which saving ${label}'s credentials creates. Answer the credentials, or run \\`fluid connect credentials ${provider.id}\\`.`,\n );\n return { stop: true };\n },\n });\n\n if (provider.targetOptions) {\n steps.push({\n id: \"mappings-automap\",\n phase: \"mappings\",\n title: \"Map exact name matches\",\n asks: true,\n run: async (canGoBack) => {\n const reply = await ask(\n {\n section: PHASE_NAMES.mappings,\n message: `Add a mapping wherever a Fluid value's name matches exactly one ${label} value? (changes live config)`,\n kind: \"select\",\n choices: [\n { title: \"Yes, map exact name matches first\", value: \"yes\" },\n { title: \"No, I'll map each one\", value: \"no\" },\n ],\n ...(answers.has(\"mappings-automap\") ? { current: autoMap ? \"yes\" : \"no\" } : {}),\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n autoMap = reply.value === \"yes\";\n return { done: true, answer: autoMap ? \"Yes\" : \"No\" };\n },\n });\n }\n\n /** The provider's values for a type, as pick-list choices (Exigo). */\n const choicesFor = async (type: string): Promise<Choice[]> => {\n if (!provider.targetOptions) return [];\n const cached = providerChoices.get(type);\n if (cached) return cached;\n let choices: Choice[] = [];\n try {\n const response = await integrations.get<{ options?: Array<{ id?: unknown; title?: string }> }>(\n `${mappingPath(connection(), type)}/exigo_options`,\n );\n const byTitle = (TARGET_FIELD[type] ?? \"\").endsWith(\"_title\");\n choices = (response?.options ?? [])\n .filter((o) => o.id !== undefined && o.id !== null)\n .map((o) => ({\n title: o.title ? `${o.title} (${String(o.id)})` : String(o.id),\n value: byTitle && o.title ? o.title : String(o.id),\n }));\n } catch {\n choices = []; // fall back to typing the value\n }\n providerChoices.set(type, choices);\n return choices;\n };\n\n /** Ask for the provider's value: a pick list for Exigo, typed otherwise. */\n const askTarget = async (\n type: string,\n message: string,\n section: string,\n canGoBack: boolean,\n offerFallback: boolean,\n current?: string,\n ) => {\n const choices = await choicesFor(type);\n const fallback: Choice[] = offerFallback\n ? [{ title: `* Use one ${label} value for every remaining ${words(type)}`, value: FALLBACK }]\n : [];\n if (choices.length > 0) {\n return ask(\n {\n section,\n message,\n kind: \"search\",\n choices: [...choices, ...fallback],\n hint: \"type to filter\",\n ...(current ? { current } : {}),\n },\n canGoBack,\n );\n }\n const targetIsName = (TARGET_FIELD[type] ?? \"\").endsWith(\"_title\");\n return ask(\n {\n section,\n message,\n kind: \"text\",\n hint: [\n `${label}'s ${words(type)} ${targetIsName ? \"name\" : \"ID\"}, from your ${label} back office`,\n offerFallback ? `or * to use one value for every remaining ${words(type)}` : \"\",\n ]\n .filter(Boolean)\n .join(\"; \"),\n ...(current ? { current } : {}),\n },\n canGoBack,\n );\n };\n\n /** Save one row, replacing the row this step saved before. */\n const saveRow = async (\n stepId: string,\n type: string,\n fields: Record<string, unknown>,\n fallback: boolean,\n ): Promise<boolean> => {\n let response: unknown;\n try {\n response = await createMapping(integrations, connection(), type, fields);\n } catch (error) {\n prompter.note(`Not saved: ${reasonOf(error)}`);\n return false;\n }\n const previous = rowsByStep.get(stepId);\n if (previous) {\n if (previous.fallback) fallbackTypes.delete(previous.type);\n if (previous.id !== undefined) {\n try {\n await deleteMapping(integrations, connection(), previous.type, previous.id);\n } catch (error) {\n prompter.note(\n `The earlier row (${previous.id}) is still there: ${reasonOf(error)}. Remove it with \\`fluid connect unmap ${provider.id} ${previous.type} ${previous.id} --yes\\`.`,\n );\n }\n }\n } else {\n result.rowsAdded += 1;\n }\n const id = rowIdOf(response, type);\n rowsByStep.set(stepId, { type, ...(id !== undefined ? { id } : {}), fallback });\n if (fallback) fallbackTypes.add(type);\n return true;\n };\n\n const valueStep = (\n report: MappingTypeReport,\n value: UnmappedValue,\n position: number,\n ): Step => {\n const type = report.type;\n const id = `map:${type}:${value.value}`;\n const name = value.title && value.title !== value.value ? `\"${value.title}\" (${value.value})` : `\"${value.value}\"`;\n const section = `${PHASE_NAMES.mappings} · ${words(type)} · ${position} of ${report.unmapped!.length}`;\n return {\n id,\n phase: \"mappings\",\n group: \"mapping-values\",\n title: `${words(type)}: ${value.title ?? value.value}`,\n asks: true,\n // A `*` row saved by an earlier answer covers the rest of the type.\n when: () => !fallbackTypes.has(type) || rowsByStep.get(id)?.fallback === true,\n run: async (canGoBack) => {\n const reply = await askTarget(\n type,\n `${label} ${words(type)} for Fluid's ${name}`,\n section,\n canGoBack,\n true,\n answers.get(id),\n );\n if (\"nav\" in reply) return reply;\n let target = reply.value.trim();\n if (target === \"\") {\n if (answers.has(id)) return { done: true, answer: answers.get(id)! };\n prompter.note(\"Choose a value, or > to skip.\");\n return { retry: true };\n }\n if (target === answers.get(id)) return { done: true, answer: target };\n const fallback = target === FALLBACK;\n if (fallback) {\n const second = await askTarget(\n type,\n `${label} ${words(type)} for every Fluid ${words(type)} without its own row`,\n section,\n canGoBack,\n false,\n );\n if (\"nav\" in second) return second.nav === \"back\" ? { retry: true } : second;\n target = second.value.trim();\n if (target === \"\") return { retry: true };\n }\n const sourceField = SOURCE_FIELD[type]!;\n const fields: Record<string, unknown> = {\n // value.value is already the title for title-keyed fields.\n [sourceField]: fallback ? FALLBACK : value.value,\n [TARGET_FIELD[type]!]: target,\n };\n if (type === \"warehouse\" && !fallback && value.id !== undefined) {\n fields[\"fluid_warehouse_id\"] = value.id;\n fields[\"source_warehouse_title\"] = value.title ?? value.value;\n }\n if (!(await saveRow(id, type, fields, fallback))) return { retry: true };\n const answer = fallback ? `* → ${target}` : target;\n prompter.note(\n fallback\n ? `Saved a * row: every other ${words(type)} maps to ${target}.`\n : `Saved: ${value.title ?? value.value} → ${target}.`,\n );\n return { done: true, answer };\n },\n };\n };\n\n /** Ranks and ship methods: the API can't list Fluid's values. */\n const byHandStep = (report: MappingTypeReport): Step => {\n const type = report.type;\n const id = `by-hand:${type}`;\n return {\n id,\n phase: \"mappings\",\n group: \"mapping-values\",\n title: `${words(type)} (by hand)`,\n asks: true,\n run: async (canGoBack) => {\n const section = `${PHASE_NAMES.mappings} · ${words(type)}`;\n const reply = await ask(\n {\n section,\n message: `Fluid can't list its ${words(type)}s, so compare \\`fluid connect mappings ${provider.id} ${type}\\` with them yourself. Add a ${words(type)} row now?`,\n kind: \"select\",\n choices: [\n { title: \"Not now\", value: \"no\" },\n { title: `Add a ${words(type)} row`, value: \"add\" },\n ],\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n if (reply.value !== \"add\") return { done: true, answer: answers.get(id) ?? \"Not now\" };\n\n const sourceField = report.sourceField ?? SOURCE_FIELD[type]!;\n const source = await ask(\n { section, message: `Fluid ${words(type)} name (or * for every one without its own row)`, kind: \"text\" },\n true,\n );\n if (\"nav\" in source) return source.nav === \"back\" ? { retry: true } : source;\n if (source.value.trim() === \"\") return { retry: true };\n const target = await askTarget(type, `${label} ${words(type)} for \"${source.value.trim()}\"`, section, true, false);\n if (\"nav\" in target) return target.nav === \"back\" ? { retry: true } : target;\n if (target.value.trim() === \"\") return { retry: true };\n const fields: Record<string, unknown> = {\n [sourceField]: source.value.trim(),\n [TARGET_FIELD[type]!]: target.value.trim(),\n };\n if (type === \"ship_method\") {\n const country = await ask(\n { section, message: \"Country code for this ship method (Enter for any country)\", kind: \"text\" },\n true,\n );\n if (\"nav\" in country) return country.nav === \"back\" ? { retry: true } : country;\n if (country.value.trim() !== \"\") fields[\"country_code\"] = country.value.trim().toUpperCase();\n }\n try {\n await createMapping(integrations, connection(), type, fields);\n } catch (error) {\n prompter.note(`Not saved: ${reasonOf(error)}`);\n return { retry: true };\n }\n result.rowsAdded += 1;\n const added = Number(answers.get(`${id}#count`) ?? \"0\") + 1;\n answers.set(`${id}#count`, String(added));\n answers.set(id, `${added} row(s) added`);\n prompter.note(`Saved: ${source.value.trim()} → ${target.value.trim()}.`);\n return { retry: true }; // ask again: another row, or Not now\n },\n };\n };\n\n steps.push({\n id: \"mappings-report\",\n phase: \"mappings\",\n title: \"Find what needs mapping\",\n asks: false,\n run: async () => {\n prompter.note(autoMap ? \"Mapping exact name matches, then checking what's left…\" : \"Checking what needs mapping…\");\n const report = await unmappedReport(integrations, connection(), provider, autoMap);\n const fresh: Step[] = [];\n let added = 0;\n for (const entry of report) {\n added += entry.autoMapped?.length ?? 0;\n if (entry.status === \"needs_mapping\" && entry.sourceField && TARGET_FIELD[entry.type]) {\n entry.unmapped!.forEach((value, i) => fresh.push(valueStep(entry, value, i + 1)));\n } else if (entry.status === \"needs_mapping\") {\n prompter.note(\n `${words(entry.type)}: ${entry.unmapped!.length} value(s) to map. Add them with \\`fluid connect map ${provider.id} ${entry.type} --set field=value --yes\\`.`,\n );\n } else if (entry.status === \"check_by_hand\" && TARGET_FIELD[entry.type]) {\n fresh.push(byHandStep(entry));\n } else if (entry.status === \"error\") {\n prompter.note(`${words(entry.type)}: couldn't read its values (${entry.error}). Check it later with \\`fluid connect unmapped ${provider.id}\\`.`);\n }\n }\n if (added > 0) {\n result.autoMapped += added;\n prompter.note(`Added ${added} row(s) by matching names.`);\n }\n const toMap = fresh.filter((s) => s.id.startsWith(\"map:\")).length;\n prompter.note(\n toMap === 0\n ? \"Every value Fluid lists is mapped.\"\n : `${toMap} value(s) to map. Pick the ${label} value for each; * covers every remaining value of that type.`,\n );\n replaceGroup(\"mapping-values\", \"mappings-report\", fresh);\n return DONE;\n },\n });\n\n // --------------------------------------------------------- settings\n const settingStep = (key: string, setting: SchemaSetting, position: number, total: number): Step => {\n const id = `setting:${key}`;\n return {\n id,\n phase: \"settings\",\n group: \"settings-values\",\n title: setting.label ?? key,\n asks: true,\n run: async (canGoBack) => {\n const summary = summarizeSettings(ci).find((s) => s.key === key);\n const current = summary ? summary.value : setting.value;\n const fallbackLabel = (value: unknown) =>\n setting.options?.find((o) => o.value === value)?.label ?? display(value);\n const question: Omit<Question, \"canGoBack\"> = {\n section: `${PHASE_NAMES.settings} · ${position} of ${total} · ${key}`,\n message: `${setting.label ?? key} (default: ${fallbackLabel(setting.value)})`,\n kind: \"text\",\n ...(setting.description ? { hint: setting.description } : {}),\n };\n let choices: Array<Choice & { raw: unknown }> | undefined;\n if (setting.options && setting.options.length > 0 && !setting.multiple) {\n choices = setting.options.map((o, i) => ({\n title: `${o.label ?? display(o.value)}${o.value === setting.value ? \" (default)\" : \"\"}`,\n value: String(i),\n raw: o.value,\n }));\n } else if (setting.type === \"boolean\") {\n choices = [\n { title: `Yes${setting.value === true ? \" (default)\" : \"\"}`, value: \"yes\", raw: true },\n { title: `No${setting.value === false ? \" (default)\" : \"\"}`, value: \"no\", raw: false },\n ];\n }\n if (choices) {\n question.kind = \"select\";\n question.choices = choices.map(({ raw: _raw, ...c }) => c);\n const now = choices.find((c) => c.raw === current);\n if (now) question.current = now.title;\n } else {\n // Nothing saved: no pre-fill, so Enter keeps it unset.\n if (current !== null && current !== undefined && current !== \"\") {\n question.current = display(current);\n }\n question.hint = [setting.description, \"JSON for lists and objects\"].filter(Boolean).join(\" · \");\n }\n\n const reply = await ask(question, canGoBack);\n if (\"nav\" in reply) return reply;\n let value: unknown;\n if (choices) {\n value = choices.find((c) => c.value === reply.value)?.raw;\n } else {\n const raw = reply.value.trim();\n if (raw === \"\") return { done: true, answer: display(current) };\n try {\n value = /^(true|false|null|-?\\d+(\\.\\d+)?)$|^[[{]/.test(raw) ? JSON.parse(raw) : raw;\n } catch {\n value = raw;\n }\n }\n if (JSON.stringify(value) === JSON.stringify(current)) {\n return { done: true, answer: fallbackLabel(value) };\n }\n try {\n ci = await updateSyncSettings(integrations, connection(), settingsPatch({ [key]: value }));\n } catch (error) {\n prompter.note(`Not saved: ${reasonOf(error)}`);\n return { retry: true };\n }\n if (!result.settingsChanged.includes(key)) result.settingsChanged.push(key);\n prompter.note(`Saved: ${setting.label ?? key} → ${fallbackLabel(value)}.`);\n return { done: true, answer: fallbackLabel(value) };\n },\n };\n };\n\n steps.push({\n id: \"settings-choice\",\n phase: \"settings\",\n title: \"Sync settings\",\n asks: true,\n run: async (canGoBack) => {\n ci = await getCompanyIntegration(integrations, connection());\n const schema = ci.settings_schema ?? {};\n const common = COMMON_SETTINGS.flatMap((key) => {\n const [entity, name] = key.split(\".\") as [string, string];\n const setting = schema[entity]?.[name];\n return setting ? [{ key, setting }] : [];\n });\n const reply = await ask(\n {\n section: PHASE_NAMES.settings,\n message: `Sync settings. A new connection starts on the defaults, and each default is safe.`,\n kind: \"select\",\n choices: [\n { title: \"Keep every setting as it is\", value: \"keep\" },\n { title: `Go through the ${common.length} common settings`, value: \"review\" },\n { title: \"Put every setting back to its default\", value: \"defaults\" },\n ],\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n if (reply.value === \"review\") {\n replaceGroup(\n \"settings-values\",\n \"settings-choice\",\n common.map(({ key, setting }, i) => settingStep(key, setting, i + 1, common.length)),\n );\n return { done: true, answer: \"Go through the common settings\" };\n }\n replaceGroup(\"settings-values\", \"settings-choice\", []);\n if (reply.value === \"defaults\") {\n try {\n ci = await updateSyncSettings(integrations, connection(), defaultsPatch(schema));\n } catch (error) {\n prompter.note(`Not saved: ${reasonOf(error)}`);\n return { retry: true };\n }\n result.settingsRestored = true;\n prompter.note(\"Every setting is back on its default.\");\n return { done: true, answer: \"Put back to defaults\" };\n }\n return { done: true, answer: \"Keep as is\" };\n },\n });\n\n // ------------------------------------------------------------- menu\n const goBack = (): boolean => {\n let previous = history.pop();\n // A regenerated report or settings pass may have removed it.\n while (previous !== undefined && indexOf(previous) < 0) previous = history.pop();\n if (previous === undefined) {\n prompter.note(\"This is the first question.\");\n return false;\n }\n skipped.delete(previous);\n index = indexOf(previous);\n targeted = previous;\n return true;\n };\n\n /** True when the walk should stop. */\n const openMenu = async (): Promise<boolean> => {\n const phases = (Object.keys(PHASE_NAMES) as Phase[]).filter((phase) =>\n steps.some((s) => s.phase === phase),\n );\n const answered = steps.filter((s) => s.asks && answers.has(s.id));\n const action = await prompter.menu(\"What next?\", [\n { title: \"Continue\", value: \"continue\" },\n { title: \"Back to the previous question\", value: \"back\", disabled: history.length === 0 },\n ...phases.map((phase) => ({ title: `Go to ${PHASE_NAMES[phase]}`, value: `phase:${phase}` })),\n { title: \"Change an answer\", value: \"change\", disabled: answered.length === 0 },\n { title: \"Finish: show the summary\", value: \"finish\" },\n { title: \"Stop for now (answers are saved)\", value: \"quit\" },\n ]);\n if (action === null || action === \"quit\") return true;\n if (action === \"back\") {\n goBack();\n } else if (action === \"finish\") {\n index = steps.length;\n } else if (action.startsWith(\"phase:\")) {\n const phase = action.slice(\"phase:\".length) as Phase;\n const first = phase === \"mappings\" ? \"mappings-start\" : steps.find((s) => s.phase === phase)!.id;\n index = indexOf(first);\n targeted = steps.find((s, i) => i >= index && s.asks)?.id;\n } else if (action === \"change\") {\n const id = await prompter.menu(\n \"Which answer?\",\n answered.map((s) => ({ title: `${s.title} — ${answers.get(s.id)}`, value: s.id })),\n );\n if (id) {\n index = indexOf(id);\n targeted = id;\n }\n }\n return false;\n };\n\n // ------------------------------------------------------------- loop\n while (index < steps.length) {\n const step = steps[index]!;\n if (step.when && !step.when()) {\n index += 1;\n continue;\n }\n if (step.asks && answers.has(step.id) && targeted !== step.id) {\n index += 1; // answered: walking forward passes it over\n continue;\n }\n const outcome = await step.run(history.length > 0);\n\n if (\"stop\" in outcome) {\n result.stopped = true;\n break;\n }\n if (\"goto\" in outcome) {\n index = indexOf(outcome.goto);\n targeted = steps[index]?.id;\n continue;\n }\n if (\"retry\" in outcome) {\n targeted = step.id;\n continue;\n }\n if (\"nav\" in outcome) {\n if (outcome.nav === \"quit\") {\n result.stopped = true;\n break;\n }\n if (outcome.nav === \"back\") {\n if (!goBack()) targeted = step.id;\n continue;\n }\n if (outcome.nav === \"skip\") {\n skipped.set(step.id, step.title);\n history.push(step.id);\n index += 1;\n continue;\n }\n targeted = step.id; // menu: come back here unless it moves elsewhere\n if (await openMenu()) {\n result.stopped = true;\n break;\n }\n continue;\n }\n if (step.asks) {\n history.push(step.id);\n skipped.delete(step.id);\n answers.set(step.id, outcome.answer ?? \"\");\n }\n targeted = undefined;\n index += 1;\n }\n\n // ---------------------------------------------------------- summary\n status = await providerStatus(integrations, provider);\n result.connected = status.connected;\n result.skipped = [...skipped.values()];\n try {\n if (ciId === null) throw new ConnectError(\"there's no connection yet\");\n const report = await unmappedReport(integrations, ciId, provider, false);\n result.mappingsDone = report.every((r) => r.status === \"covered\");\n result.stillUnmapped = report\n .filter((r) => r.status !== \"covered\")\n .map((r) => ({ type: r.type, count: r.unmapped?.length ?? null, status: r.status }));\n } catch (error) {\n prompter.note(`Couldn't check the mappings: ${reasonOf(error)}`);\n }\n\n const lines = [\n `${label}: ${result.connected ? \"connected\" : \"not connected yet\"}.`,\n result.credentialsSaved ? \"Credentials saved and tested.\" : \"\",\n result.autoMapped + result.rowsAdded > 0\n ? `Mapping rows added: ${result.rowsAdded}${result.autoMapped > 0 ? `, plus ${result.autoMapped} by name` : \"\"}.`\n : \"\",\n result.mappingsDone\n ? \"Every mapping type is covered.\"\n : result.stillUnmapped.length > 0\n ? `Still to map or check: ${result.stillUnmapped\n .map((r) => `${words(r.type)}${r.count ? ` (${r.count})` : r.status === \"check_by_hand\" ? \" (check by hand)\" : \"\"}`)\n .join(\", \")}. See \\`fluid connect unmapped ${provider.id}\\`.`\n : \"\",\n result.settingsRestored ? \"Every sync setting is on its default.\" : \"\",\n result.settingsChanged.length > 0 ? `Settings changed: ${result.settingsChanged.join(\", \")}.` : \"\",\n result.skipped.length > 0 ? `Skipped: ${result.skipped.join(\"; \")}.` : \"\",\n `Run \\`fluid connect walk ${provider.id}\\` again to pick up where you left off.`,\n ].filter(Boolean);\n prompter.note(`\\n${lines.join(\"\\n\")}`);\n return result;\n}\n","import type { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport type { CommandContext, FluidPlugin } from \"@fluid-app/fluid-cli\";\nimport {\n assertInteractive,\n createDomainCommand,\n terminalPrompter,\n} from \"@fluid-app/fluid-cli\";\n\nimport {\n companyIntegrationId,\n createMapping,\n deleteMapping,\n getCompanyIntegration,\n installProvider,\n integrationsBase,\n listMappings,\n mappingOptions,\n providerStatus,\n saveCredentials,\n updateSyncSettings,\n type ConnectClient,\n} from \"./api.js\";\nimport { ConnectError, PROVIDER_IDS, providerOf } from \"./providers.js\";\nimport { defaultsPatch, parseAssignments, syncSettingsPatch } from \"./settings.js\";\nimport { summarizeSettings } from \"./summary.js\";\nimport { unmappedReport } from \"./unmapped.js\";\nimport { walkConnect } from \"./walk.js\";\n\nasync function fluidClient(ctx: CommandContext): Promise<ConnectClient> {\n return (await ctx.getClient()) as unknown as ConnectClient;\n}\n\nasync function integrationsClient(ctx: CommandContext): Promise<ConnectClient> {\n return (await ctx.getClientFor(integrationsBase())) as unknown as ConnectClient;\n}\n\n/**\n * Changes to a live connection need an explicit yes. Scripts and agents\n * pass `--yes`; an interactive terminal is asked. With neither, nothing runs.\n */\nexport async function confirmChange(\n question: string,\n yes: boolean,\n interactive: boolean = Boolean(process.stdin.isTTY),\n): Promise<void> {\n if (yes) return;\n if (!interactive) throw new ConnectError(`${question} Pass --yes to confirm.`);\n const answer = await prompts({\n type: \"confirm\",\n name: \"ok\",\n message: question,\n initial: false,\n });\n if (!answer.ok) throw new ConnectError(\"Cancelled.\");\n}\n\n/** Print a failure as JSON on stderr and exit non-zero. A failed connection\n * test (422) carries the provider's reason in `errors`. */\nfunction fail(err: unknown): void {\n const status =\n typeof (err as { status?: unknown }).status === \"number\"\n ? (err as { status: number }).status\n : undefined;\n const body = (err as { body?: Record<string, unknown> | null }).body;\n const detail =\n body && (body[\"errors\"] ?? body[\"error\"] ?? body[\"error_message\"]);\n const message =\n typeof detail === \"string\"\n ? detail\n : err instanceof Error\n ? err.message\n : String(err);\n process.stderr.write(\n JSON.stringify({\n error: message,\n ...(status ? { status } : {}),\n ...(detail && typeof detail === \"object\" ? { details: detail } : {}),\n }) + \"\\n\",\n );\n process.exitCode = 1;\n}\n\nfunction run(action: () => Promise<void>): Promise<void> {\n return action().catch(fail);\n}\n\nconst collect = (value: string, previous: string[] = []): string[] => [\n ...previous,\n value,\n];\n\nexport function registerConnectCommands(\n parent: Command,\n ctx: CommandContext,\n): void {\n parent\n .command(\"list\")\n .description(`Show ${PROVIDER_IDS.join(\", \")} and whether each is installed`)\n .action(() =>\n run(async () => {\n const integrations = await integrationsClient(ctx);\n ctx.output(\n await Promise.all(\n PROVIDER_IDS.map((id) => providerStatus(integrations, providerOf(id))),\n ),\n );\n }),\n );\n\n parent\n .command(\"install <provider>\")\n .description(\"Install the provider's droplet, which creates its connection (changes live config)\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, opts: { yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n const status = await providerStatus(await integrationsClient(ctx), provider);\n if (status.installed) {\n ctx.output({ ...status, alreadyInstalled: true });\n return;\n }\n await confirmChange(`Install ${provider.label} for this company?`, opts.yes);\n ctx.output(await installProvider(await fluidClient(ctx), provider, status.dropletUuid));\n }),\n );\n\n parent\n .command(\"show <provider>\")\n .description(\"Show the connection: state, supported mappings, and every sync setting with its current value\")\n .action((providerId: string) =>\n run(async () => {\n const provider = providerOf(providerId);\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n const ci = await getCompanyIntegration(integrations, ciId);\n ctx.output({\n provider: provider.id,\n companyIntegrationId: ci.id,\n initialized: ci.initialized ?? null,\n environment: ci.environment ?? null,\n liveSyncState: ci.live_sync_state ?? null,\n liveSyncDegradedReason: ci.live_sync_degraded_reason ?? null,\n credentialGroups: provider.credentialGroups,\n supportedMappings: ci.supported_mappings ?? [],\n settings: summarizeSettings(ci),\n });\n }),\n );\n\n parent\n .command(\"credentials <provider>\")\n .description(\"Save credentials; the connection is tested before anything is saved, and created if the install has not yet (changes live config). Values here land in shell history; `fluid connect walk` asks for passwords hidden\")\n .option(\"--set <key=value>\", \"A credential, e.g. api.username=alice or mssql.dataserver=sql.example.com (repeatable)\", collect, [])\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, opts: { set: string[]; yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n if (opts.set.length === 0) {\n throw new ConnectError(\n `Pass credentials with --set, e.g. --set api.username=alice. ${provider.label} uses: ${provider.credentialGroups.join(\", \")}.`,\n );\n }\n const authSettings = parseAssignments(opts.set, { strings: true });\n const integrations = await integrationsClient(ctx);\n await confirmChange(`Save new ${provider.label} credentials?`, opts.yes);\n ctx.output(await saveCredentials(integrations, await fluidClient(ctx), provider, authSettings));\n }),\n );\n\n parent\n .command(\"settings <provider>\")\n .description(\"List sync settings, change them with --set entity.key=value, or restore defaults with --defaults (changes live config)\")\n .option(\"--set <key=value>\", \"A setting from `fluid connect show`, e.g. order.refund_handling=hold (repeatable)\", collect, [])\n .option(\"--defaults\", \"Restore settings to their defaults: all of them, or only those named with --only\", false)\n .option(\"--only <key>\", \"With --defaults, restore just this setting, e.g. order.refund_handling (repeatable)\", collect, [])\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, opts: { set: string[]; defaults: boolean; only: string[]; yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n if (opts.defaults && opts.set.length > 0) {\n throw new ConnectError(\"Use --set or --defaults, not both.\");\n }\n if (opts.only.length > 0 && !opts.defaults) {\n throw new ConnectError(\"--only goes with --defaults.\");\n }\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n if (opts.defaults) {\n const ci = await getCompanyIntegration(integrations, ciId);\n const patch = defaultsPatch(ci.settings_schema ?? {}, opts.only);\n const what = opts.only.length > 0 ? `${opts.only.length} setting(s)` : \"every sync setting\";\n await confirmChange(`Restore ${what} to the default for ${provider.label}?`, opts.yes);\n ctx.output(summarizeSettings(await updateSyncSettings(integrations, ciId, patch)));\n return;\n }\n if (opts.set.length === 0) {\n ctx.output(summarizeSettings(await getCompanyIntegration(integrations, ciId)));\n return;\n }\n const patch = syncSettingsPatch(opts.set);\n await confirmChange(`Change ${opts.set.length} ${provider.label} sync setting(s)?`, opts.yes);\n ctx.output(summarizeSettings(await updateSyncSettings(integrations, ciId, patch)));\n }),\n );\n\n parent\n .command(\"walk <provider>\")\n .description(\n \"Set up the connection question by question in the terminal: install, credentials, mappings, and settings. Go back with <, skip with >, open the menu with ?; each answer is saved as you go (changes live config)\",\n )\n .action((providerId: string) =>\n run(async () => {\n assertInteractive(\n \"`install`, `credentials`, `unmapped`, `map`, and `settings` with --yes (see `fluid connect --help`)\",\n );\n const provider = providerOf(providerId);\n ctx.output(\n await walkConnect({\n fluid: await fluidClient(ctx),\n integrations: await integrationsClient(ctx),\n provider,\n prompter: terminalPrompter(),\n }),\n );\n }),\n );\n\n parent\n .command(\"unmapped <provider>\")\n .description(\"List only the data mappings a person still has to add; --auto-map first maps exact name matches (Exigo)\")\n .option(\"--auto-map\", \"Exigo: add rows where a Fluid value's name matches exactly one Exigo value (changes live config)\", false)\n .option(\"--yes\", \"Confirm --auto-map without a prompt\", false)\n .action((providerId: string, opts: { autoMap: boolean; yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n if (opts.autoMap) {\n if (!provider.targetOptions) {\n throw new ConnectError(\n `--auto-map needs ${provider.label}'s own values, which only Exigo lists. Run without it to see what to map.`,\n );\n }\n await confirmChange(`Add ${provider.label} mappings for exact name matches?`, opts.yes);\n }\n const report = await unmappedReport(integrations, ciId, provider, opts.autoMap);\n ctx.output({\n provider: provider.id,\n done: report.every((r) => r.status === \"covered\"),\n mappings: report,\n });\n // A type that couldn't be checked makes the report incomplete.\n if (report.some((r) => r.status === \"error\")) process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"mappings <provider> <type>\")\n .description(\"List one mapping type's rows (e.g. customer_type, warehouse); --options lists the values to choose from\")\n .option(\"--options\", \"List the Fluid values, and for Exigo the Exigo values, instead of the saved rows\", false)\n .action((providerId: string, type: string, opts: { options: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n ctx.output(\n opts.options\n ? await mappingOptions(integrations, ciId, type, provider)\n : await listMappings(integrations, ciId, type),\n );\n }),\n );\n\n parent\n .command(\"map <provider> <type>\")\n .description(\"Add a mapping row, e.g. --set source_type_slug=rep --set target_type_id=2 (changes live config)\")\n .option(\"--set <field=value>\", \"A field of the mapping row (repeatable)\", collect, [])\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, type: string, opts: { set: string[]; yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n if (opts.set.length === 0) {\n throw new ConnectError(\"Pass the row's fields with --set field=value.\");\n }\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n await confirmChange(`Add a ${type} mapping to ${provider.label}?`, opts.yes);\n ctx.output(await createMapping(integrations, ciId, type, parseAssignments(opts.set)));\n }),\n );\n\n parent\n .command(\"unmap <provider> <type> <id>\")\n .description(\"Remove one mapping row (changes live config)\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, type: string, id: string, opts: { yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n const rowId = Number(id);\n if (!Number.isInteger(rowId) || rowId <= 0) {\n throw new ConnectError(`\"${id}\" isn't a mapping row id.`);\n }\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n await confirmChange(`Remove ${type} mapping ${rowId} from ${provider.label}?`, opts.yes);\n ctx.output(await deleteMapping(integrations, ciId, type, rowId));\n }),\n );\n}\n\nconst plugin: FluidPlugin = {\n name: \"@fluid-app/fluid-cli-connect\",\n version: \"0.1.0\",\n register(ctx) {\n ctx.program.addCommand(\n createDomainCommand(\n \"connect\",\n \"Connect Exigo, InfoTrax, ByDesign, or Pillars: credentials, data mappings, and sync settings\",\n registerConnectCommands,\n ),\n );\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["setPath"],"sources":["../src/providers.ts","../src/api.ts","../src/settings.ts","../src/summary.ts","../src/unmapped.ts","../src/walk.ts","../src/index.ts"],"sourcesContent":["/**\n * The back-office platforms Fluid Connect links to Fluid.\n *\n * Only what the API doesn't report lives here: the slug fluid-integrations\n * names each provider by, the droplet name the Fluid marketplace lists it\n * under, and which credential groups its connection needs. Settings,\n * supported mappings, and mapping options are read live from the API.\n */\n/** One credential the connection needs. `key` is the dotted path in\n * `auth_settings` (the same names `fluid connect credentials --set` takes). */\nexport interface CredentialField {\n key: string;\n label: string;\n /** Typed without echo, and never shown back. */\n secret?: boolean;\n /** Fluid has a default for it, so it can be left blank. */\n optional?: boolean;\n}\n\nconst MSSQL_CREDENTIALS: readonly CredentialField[] = [\n { key: \"mssql.dataserver\", label: \"SQL Server host (data server)\" },\n { key: \"mssql.database\", label: \"SQL Server database name\" },\n { key: \"mssql.username\", label: \"SQL Server login username\" },\n { key: \"mssql.password\", label: \"SQL Server login password\", secret: true },\n];\n\nexport const PROVIDERS = {\n exigo: {\n label: \"Exigo\",\n slug: \"exigo\",\n dropletName: \"Exigo\",\n // Reads changes from Exigo's SQL Server; writes through Exigo's API.\n credentialGroups: [\"api\", \"mssql\"],\n credentials: [\n { key: \"api.company\", label: \"Exigo company login (company code)\" },\n { key: \"api.username\", label: \"Exigo API service-account username\" },\n { key: \"api.password\", label: \"Exigo API service-account password\", secret: true },\n ...MSSQL_CREDENTIALS,\n ],\n // Exigo also asks `environment`, and `sandbox_id` outside production.\n environments: true,\n // Exigo's own values for the target side of a mapping.\n targetOptions: true,\n },\n bydesign: {\n label: \"ByDesign\",\n slug: \"bydesign\",\n dropletName: \"ByDesign\",\n credentialGroups: [\"api\"],\n credentials: [\n { key: \"api.company\", label: \"ByDesign tenant (company) code\" },\n { key: \"api.username\", label: \"ByDesign API service-account username\" },\n { key: \"api.password\", label: \"ByDesign API service-account password\", secret: true },\n { key: \"api.api_url\", label: \"Freedom API host (blank for Fluid's default)\", optional: true },\n ],\n environments: false,\n targetOptions: false,\n },\n infotrax: {\n label: \"InfoTrax\",\n slug: \"infotrax\",\n dropletName: \"Infotrax\",\n credentialGroups: [\"api\", \"mssql\"],\n credentials: MSSQL_CREDENTIALS,\n environments: false,\n targetOptions: false,\n },\n pillars: {\n label: \"Pillars\",\n slug: \"pillars\",\n dropletName: \"Pillars\",\n credentialGroups: [\"api\"],\n credentials: [\n { key: \"api.token\", label: \"Pillars API token\", secret: true },\n { key: \"api.api_url\", label: \"Pillars API base URL\" },\n ],\n environments: false,\n targetOptions: false,\n },\n} as const satisfies Record<\n string,\n {\n label: string;\n slug: string;\n dropletName: string;\n credentialGroups: readonly string[];\n credentials: readonly CredentialField[];\n environments: boolean;\n targetOptions: boolean;\n }\n>;\n\nexport type ProviderId = keyof typeof PROVIDERS;\nexport type Provider = (typeof PROVIDERS)[ProviderId];\n\nexport const PROVIDER_IDS = Object.keys(PROVIDERS) as ProviderId[];\n\n/** Scopes the Connect droplet is installed with (the same set Mist uses). */\nexport const CONNECT_DROPLET_SCOPES = [\n \"main\",\n \"members\",\n \"prospects\",\n \"products\",\n \"orders\",\n \"users\",\n \"settings\",\n] as const;\n\nexport class ConnectError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConnectError\";\n }\n}\n\nexport function providerOf(id: string): Provider & { id: ProviderId } {\n const key = id.trim().toLowerCase();\n if (!(key in PROVIDERS)) {\n throw new ConnectError(\n `Unknown provider \"${id}\". Use one of: ${PROVIDER_IDS.join(\", \")}.`,\n );\n }\n return { id: key as ProviderId, ...PROVIDERS[key as ProviderId] };\n}\n","import {\n CONNECT_DROPLET_SCOPES,\n ConnectError,\n type Provider,\n type ProviderId,\n} from \"./providers.js\";\n\n/** The slice of the CLI's API client these calls use. */\nexport interface ConnectClient {\n get<T>(endpoint: string, params?: Record<string, unknown>): Promise<T>;\n post<T>(endpoint: string, body?: unknown): Promise<T>;\n put<T>(endpoint: string, body?: unknown): Promise<T>;\n delete<T>(endpoint: string): Promise<T>;\n}\n\nexport const DEFAULT_INTEGRATIONS_BASE =\n \"https://fluid-integrations-106074092699.us-west3.run.app\";\n\n/** fluid-integrations, where Connect's configuration lives. It accepts the\n * same Fluid API token the CLI sends to api.fluid.app. */\nexport function integrationsBase(): string {\n return (process.env[\"FLUID_INTEGRATIONS_BASE\"] ?? DEFAULT_INTEGRATIONS_BASE)\n .trim()\n .replace(/\\/$/, \"\");\n}\n\ninterface IntegrationWire {\n id?: number;\n name?: string;\n droplet_uuid?: string | null;\n company_integration_id?: number | null;\n is_connected?: boolean | null;\n supported_entities?: string[];\n supported_sync_modes?: string[];\n}\n\nexport interface ProviderStatus {\n provider: ProviderId;\n label: string;\n /** False when the company has no connection to the provider yet. */\n installed: boolean;\n connected: boolean;\n companyIntegrationId: number | null;\n /** The official droplet for the provider, as fluid-integrations names it. */\n dropletUuid: string | null;\n /** fluid-integrations' id for the provider, used to create a connection. */\n integrationId: number | null;\n}\n\n/** The provider's integration as fluid-integrations sees this company. */\n/**\n * fluid-integrations has no record of a company until its first Connect\n * droplet is installed in Fluid, so it answers a new company's token with\n * 409 company_not_installed. Nothing is installed yet, which is what every\n * caller needs to know; installing goes through Fluid, not this service.\n */\nexport function isCompanyNotInstalled(error: unknown): boolean {\n const e = error as { status?: unknown; body?: { error_code?: unknown } | null };\n return e.status === 409 && e.body?.error_code === \"company_not_installed\";\n}\n\nexport async function providerStatus(\n integrations: ConnectClient,\n provider: Provider & { id: ProviderId },\n): Promise<ProviderStatus> {\n let wire: IntegrationWire;\n try {\n wire = await integrations.get<IntegrationWire>(\n `/api/v1/integrations/${provider.slug}`,\n );\n } catch (error) {\n if (isCompanyNotInstalled(error)) {\n return {\n provider: provider.id,\n label: provider.label,\n installed: false,\n connected: false,\n companyIntegrationId: null,\n dropletUuid: null,\n integrationId: null,\n };\n }\n if ((error as { status?: number }).status === 404) {\n throw new ConnectError(\n `${provider.label} isn't available on fluid-integrations for this company.`,\n );\n }\n throw error;\n }\n const ciId = wire.company_integration_id ?? null;\n return {\n provider: provider.id,\n label: provider.label,\n installed: ciId !== null,\n connected: wire.is_connected === true,\n companyIntegrationId: ciId,\n dropletUuid: wire.droplet_uuid ?? null,\n integrationId: wire.id ?? null,\n };\n}\n\n/** The company integration id, or a ConnectError saying to install first. */\nexport async function companyIntegrationId(\n integrations: ConnectClient,\n provider: Provider & { id: ProviderId },\n): Promise<number> {\n const status = await providerStatus(integrations, provider);\n if (status.companyIntegrationId === null) {\n throw new ConnectError(\n `${provider.label} isn't installed for this company. Run \\`fluid connect install ${provider.id} --yes\\` first.`,\n );\n }\n return status.companyIntegrationId;\n}\n\nexport interface CompanyIntegrationWire {\n id: number;\n name?: string;\n initialized?: boolean;\n environment?: string | null;\n sync_settings?: Record<string, unknown>;\n settings_schema?: Record<string, Record<string, SchemaSetting>>;\n supported_mappings?: string[];\n live_sync_state?: string | null;\n live_sync_degraded_reason?: string | null;\n}\n\nexport interface SchemaSetting {\n type?: string;\n value?: unknown;\n label?: string;\n description?: string;\n options?: Array<{ value: unknown; label?: string }>;\n multiple?: boolean;\n depends_on?: string | string[];\n}\n\nexport async function getCompanyIntegration(\n integrations: ConnectClient,\n ciId: number,\n): Promise<CompanyIntegrationWire> {\n return integrations.get<CompanyIntegrationWire>(\n `/api/v1/company_integrations/${ciId}`,\n );\n}\n\n/** Save credentials. fluid-integrations tests the connection with them and\n * answers 422 with the reason when it fails, so nothing broken is saved. */\nexport async function setCredentials(\n integrations: ConnectClient,\n ciId: number,\n authSettings: Record<string, unknown>,\n): Promise<{ saved: true }> {\n await integrations.put(`/api/v1/company_integrations/${ciId}`, {\n company_integration: { auth_settings: authSettings },\n });\n return { saved: true };\n}\n\n/**\n * Save credentials, creating the connection when the droplet is installed\n * but its install hasn't created the connection yet (the same request\n * Fluid's admin makes). Without an installed droplet nothing is created, so\n * `install` still installs it. Either way fluid-integrations tests the\n * connection first and answers 422 with the reason when it fails, so\n * nothing broken is saved.\n */\nexport async function saveCredentials(\n integrations: ConnectClient,\n fluid: ConnectClient,\n provider: Provider & { id: ProviderId },\n authSettings: Record<string, unknown>,\n): Promise<{ saved: true; created: boolean; companyIntegrationId: number }> {\n const status = await providerStatus(integrations, provider);\n if (status.companyIntegrationId !== null) {\n await setCredentials(integrations, status.companyIntegrationId, authSettings);\n return { saved: true, created: false, companyIntegrationId: status.companyIntegrationId };\n }\n // The droplet `install` would have installed: the one fluid-integrations\n // names, else the catalog's unambiguous name match.\n const dropletUuid =\n status.dropletUuid ?? (await findDropletUuid(fluid, provider).catch(() => null));\n if (\n status.integrationId === null ||\n dropletUuid === null ||\n !(await dropletInstalled(fluid, dropletUuid))\n ) {\n throw new ConnectError(\n `${provider.label} isn't installed for this company. Run \\`fluid connect install ${provider.id} --yes\\` first.`,\n );\n }\n const created = await integrations.post<{ id?: number; data?: { id?: number } }>(\n \"/api/v1/company_integrations\",\n {\n company_integration: {\n integration_id: status.integrationId,\n name: provider.label,\n droplet_uuid: dropletUuid,\n auth_settings: authSettings,\n },\n },\n );\n const id =\n created?.id ??\n created?.data?.id ??\n (await providerStatus(integrations, provider)).companyIntegrationId;\n if (typeof id !== \"number\") {\n throw new ConnectError(`${provider.label} accepted the credentials, but the connection's id didn't come back. Run \\`fluid connect show ${provider.id}\\`.`);\n }\n return { saved: true, created: true, companyIntegrationId: id };\n}\n\n/** Merge a sync-settings patch into the saved settings. */\nexport async function updateSyncSettings(\n integrations: ConnectClient,\n ciId: number,\n patch: Record<string, unknown>,\n): Promise<CompanyIntegrationWire> {\n await integrations.put(`/api/v1/company_integrations/${ciId}`, {\n company_integration: { sync_settings: patch },\n });\n return getCompanyIntegration(integrations, ciId);\n}\n\n/** A mapping type as its route names it, e.g. `customer_type`. */\n/**\n * fluid-integrations names its mapping cards with a `_title` suffix\n * (`customer_type_title`); the routes, and so the commands, use the bare type\n * (`customer_type`). Accept either and use the bare one.\n */\nexport function mappingType(name: string): string {\n return name.replace(/_title$/, \"\");\n}\n\nexport function mappingPath(ciId: number, name: string): string {\n const type = mappingType(name);\n if (!/^[a-z_]+$/.test(type)) {\n throw new ConnectError(\n `\"${type}\" isn't a mapping type. Use names like customer_type or warehouse (see \\`fluid connect show\\`).`,\n );\n }\n return `/api/v1/company_integrations/${ciId}/${type}_mappings`;\n}\n\nexport async function listMappings(\n integrations: ConnectClient,\n ciId: number,\n type: string,\n): Promise<unknown> {\n return integrations.get(mappingPath(ciId, type));\n}\n\n/** Values to choose from: Fluid's for the source side, and for Exigo also\n * Exigo's own for the target side. */\nexport async function mappingOptions(\n integrations: ConnectClient,\n ciId: number,\n type: string,\n provider: Provider,\n): Promise<{ fluid: unknown; provider?: unknown }> {\n // Some types (ship methods) list only Exigo's values, so a missing\n // source_options leaves `fluid` null rather than failing the command.\n let fluid: unknown = null;\n try {\n fluid = await integrations.get(`${mappingPath(ciId, type)}/source_options`);\n } catch (error) {\n if ((error as { status?: number }).status !== 404) throw error;\n if (!provider.targetOptions) throw error;\n }\n if (!provider.targetOptions) return { fluid };\n return {\n fluid,\n provider: await integrations.get(\n `${mappingPath(ciId, type)}/exigo_options`,\n ),\n };\n}\n\n/** Create one mapping row. `fields` are the row's attributes, wrapped in\n * the singular resource key the API expects (`customer_type_mapping`). */\nexport async function createMapping(\n integrations: ConnectClient,\n ciId: number,\n type: string,\n fields: Record<string, unknown>,\n): Promise<unknown> {\n return integrations.post(mappingPath(ciId, type), {\n [`${mappingType(type)}_mapping`]: fields,\n });\n}\n\nexport async function deleteMapping(\n integrations: ConnectClient,\n ciId: number,\n type: string,\n id: number,\n): Promise<{ deleted: true; id: number }> {\n await integrations.delete(`${mappingPath(ciId, type)}/${id}`);\n return { deleted: true, id };\n}\n\ninterface InstallationListWire {\n droplet_installations?: Array<{ droplet_uuid?: string; active?: boolean }>;\n meta?: { pagination?: { total_pages?: number } };\n}\n\n/** Whether the company has the droplet installed and active in Fluid (one\n * pending uninstall is listed, but inactive). */\nexport async function dropletInstalled(\n fluid: ConnectClient,\n dropletUuid: string,\n): Promise<boolean> {\n for (let page = 1; page <= CATALOG_PAGES; page += 1) {\n const data = await fluid.get<InstallationListWire>(\"/api/droplet_installations\", {\n page,\n per_page: 100,\n });\n const rows = data.droplet_installations ?? [];\n if (rows.some((row) => row.droplet_uuid === dropletUuid && row.active !== false)) return true;\n const totalPages = data.meta?.pagination?.total_pages ?? 1;\n if (page >= totalPages || rows.length < 100) break;\n }\n return false;\n}\n\ninterface DropletListWire {\n droplets?: Array<{ uuid?: string; name?: string }>;\n meta?: { pagination?: { total_pages?: number } };\n}\n\nconst CATALOG_PAGES = 10;\n\n/** Install the provider's droplet in Fluid, which creates its company\n * integration on fluid-integrations. */\nexport async function installProvider(\n fluid: ConnectClient,\n provider: Provider,\n /** The official droplet from fluid-integrations, when it names one. */\n officialUuid: string | null = null,\n): Promise<{ installed: true; dropletUuid: string }> {\n const dropletUuid = officialUuid ?? (await findDropletUuid(fluid, provider));\n await fluid.post(\"/api/droplet_installations\", {\n droplet_uuid: dropletUuid,\n scopes: CONNECT_DROPLET_SCOPES,\n });\n return { installed: true, dropletUuid };\n}\n\nasync function findDropletUuid(\n fluid: ConnectClient,\n provider: Provider,\n): Promise<string> {\n const wanted = provider.dropletName.toLowerCase();\n const matches = new Set<string>();\n for (let page = 1; page <= CATALOG_PAGES; page += 1) {\n const data = await fluid.get<DropletListWire>(\"/api/droplets\", {\n page,\n per_page: 100,\n });\n for (const droplet of data.droplets ?? []) {\n if (droplet.uuid && droplet.name?.trim().toLowerCase() === wanted) matches.add(droplet.uuid);\n }\n const totalPages = data.meta?.pagination?.total_pages ?? 1;\n if (page >= totalPages || (data.droplets ?? []).length < 100) break;\n }\n // A company's own droplet can share the name, so a name alone must be\n // unambiguous before it's installed with Connect's scopes.\n if (matches.size === 1) return [...matches][0]!;\n if (matches.size > 1) {\n throw new ConnectError(\n `More than one droplet is named ${provider.dropletName}, and fluid-integrations didn't say which is ${provider.label}'s. Ask Fluid to check the ${provider.label} integration's droplet.`,\n );\n }\n throw new ConnectError(\n `The ${provider.label} droplet isn't in this company's droplet catalog. Ask Fluid to make it available.`,\n );\n}\n","import { ConnectError } from \"./providers.js\";\n\n/**\n * Parse `--set` values into the nested object the API takes.\n *\n * `api.username=alice` → `{ api: { username: \"alice\" } }`. Values that read\n * as JSON (true, 42, [\"a\",\"b\"], {\"x\":1}) are parsed; anything else stays a\n * string, so a password like `abc123` is never mangled.\n */\nexport function parseAssignments(\n assignments: readonly string[],\n /** Keep every value a string, as credentials need: a password like\n * `123456` or `true` must reach the API exactly as typed. */\n opts: { strings?: boolean } = {},\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const assignment of assignments) {\n const at = assignment.indexOf(\"=\");\n if (at <= 0) {\n throw new ConnectError(\n `\"${assignment}\" isn't key=value. Use dots for nesting, e.g. api.username=alice.`,\n );\n }\n const path = assignment.slice(0, at).split(\".\");\n if (path.some((part) => part.length === 0)) {\n throw new ConnectError(`\"${assignment}\" has an empty key segment.`);\n }\n if (path.some((part) => UNSAFE_KEYS.has(part))) {\n throw new ConnectError(`\"${assignment}\" uses a reserved key name.`);\n }\n const raw = assignment.slice(at + 1);\n setPath(out, path, opts.strings ? raw : parseValue(raw));\n }\n return out;\n}\n\n// Keys that would reach Object.prototype through a plain object.\nconst UNSAFE_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nfunction parseValue(raw: string): unknown {\n if (/^(true|false|null|-?\\d+(\\.\\d+)?)$/.test(raw) || /^[[{]/.test(raw)) {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n return raw;\n}\n\nfunction setPath(\n target: Record<string, unknown>,\n path: string[],\n value: unknown,\n): void {\n let node = target;\n for (const key of path.slice(0, -1)) {\n const next = node[key];\n if (typeof next !== \"object\" || next === null || Array.isArray(next)) {\n node[key] = {};\n }\n node = node[key] as Record<string, unknown>;\n }\n node[path[path.length - 1]!] = value;\n}\n\n/**\n * Sync settings are saved per entity as the schema entry itself, with the\n * chosen value under `value`: `entities.<entity>.settings.<key>.value`.\n * `--set` takes the short `<entity>.<key>=value` form `fluid connect\n * settings` prints, and this expands it. Exigo's `sponsor_tree` is the one\n * top-level setting (`sync_settings.sponsor_tree.value`); write it as\n * `sponsor_tree.sponsor_tree=value`.\n */\nexport function syncSettingsPatch(\n assignments: readonly string[],\n): Record<string, unknown> {\n const entries: Array<[string, unknown]> = [];\n for (const assignment of assignments) {\n const parsed = parseAssignments([assignment]);\n const [group, values] = Object.entries(parsed)[0]!;\n if (typeof values !== \"object\" || values === null || Array.isArray(values)) {\n throw new ConnectError(\n `\"${assignment}\" needs an entity and a setting, e.g. order.refund_handling=hold.`,\n );\n }\n for (const [key, value] of Object.entries(values)) {\n entries.push([`${group}.${key}`, value]);\n }\n }\n return patchFromEntries(entries);\n}\n\n/**\n * A patch that puts settings back to their schema defaults: all of them,\n * or only the `<entity>.<key>` names in `only`. Unknown names are refused\n * rather than silently skipped.\n */\nexport function defaultsPatch(\n schema: Record<string, Record<string, { value?: unknown }>>,\n only: readonly string[] = [],\n): Record<string, unknown> {\n const all = Object.entries(schema).flatMap(([entity, settings]) =>\n Object.entries(settings).map(\n ([key, setting]) => [`${entity}.${key}`, setting.value] as [string, unknown],\n ),\n );\n // A setting whose schema has no default is left as it is: there's\n // nothing to restore, and sending null would wipe a saved value.\n const withDefault = all.filter(([, value]) => value !== undefined);\n if (only.length === 0) return patchFromEntries(withDefault);\n const known = new Map(all);\n const unknown = only.filter((key) => !known.has(key));\n if (unknown.length > 0) {\n throw new ConnectError(\n `Unknown setting(s): ${unknown.join(\", \")}. List them with \\`fluid connect settings <provider>\\`.`,\n );\n }\n const noDefault = only.filter((key) => known.get(key) === undefined);\n if (noDefault.length > 0) {\n throw new ConnectError(`No default to restore for: ${noDefault.join(\", \")}.`);\n }\n return patchFromEntries(only.map((key) => [key, known.get(key)]));\n}\n\n/** Settings worth deciding before the first sync, in the order the setup\n * guides list them. A provider without one of them skips it. */\nexport const COMMON_SETTINGS = [\n \"customer.duplicate_customer_handling\",\n \"customer.unverified_customer_handling\",\n \"customer.customer_creation_sync\",\n \"customer.customer_changelog_sync\",\n \"order.refund_handling\",\n \"order.duplicate_order_handling\",\n \"order.order_creation_sync\",\n \"order.order_changelog_sync\",\n \"product.new_inactive\",\n \"product.warehouseless_product_handling\",\n \"rank.sync_ranks\",\n \"rank.pulled_from\",\n \"sponsor_tree.sponsor_tree\",\n \"customer.fetch_mode\",\n \"order_payment.skip_payment_on_export\",\n] as const;\n\n/** A patch setting `<entity>.<key>` names to values exactly as given (no\n * parsing), in the shape `--set` and `--defaults` send. */\nexport function settingsPatch(\n values: Record<string, unknown>,\n): Record<string, unknown> {\n return patchFromEntries(Object.entries(values));\n}\n\nfunction patchFromEntries(\n entries: ReadonlyArray<[string, unknown]>,\n): Record<string, unknown> {\n const entities: Record<string, { settings: Record<string, unknown> }> = {};\n const patch: Record<string, unknown> = {};\n for (const [name, value] of entries) {\n const at = name.indexOf(\".\");\n const group = name.slice(0, at);\n const key = name.slice(at + 1);\n if (group === \"sponsor_tree\" && key === \"sponsor_tree\") {\n patch[\"sponsor_tree\"] = { value };\n continue;\n }\n entities[group] ??= { settings: {} };\n entities[group].settings[key] = { value };\n }\n if (Object.keys(entities).length > 0) patch[\"entities\"] = entities;\n return patch;\n}\n","import type { CompanyIntegrationWire, SchemaSetting } from \"./api.js\";\n\nexport interface SettingSummary {\n /** Pass to `fluid connect settings --set <key>=<value>`. */\n key: string;\n label: string | null;\n description: string | null;\n type: string | null;\n value: unknown;\n default: unknown;\n options?: unknown[];\n multiple?: boolean;\n dependsOn?: string | string[];\n}\n\n/**\n * Every sync setting the provider supports, with the value saved for this\n * company next to the default. Saved values live at\n * `entities.<entity>.settings.<key>.value` (Exigo's `sponsor_tree` at\n * `sponsor_tree.value`); a setting never saved reads as its default.\n */\nexport function summarizeSettings(\n ci: CompanyIntegrationWire,\n): SettingSummary[] {\n const schema = ci.settings_schema ?? {};\n const saved = ci.sync_settings ?? {};\n const out: SettingSummary[] = [];\n for (const [entity, settings] of Object.entries(schema)) {\n for (const [name, setting] of Object.entries(settings)) {\n out.push(summarize(entity, name, setting, savedValue(saved, entity, name)));\n }\n }\n return out;\n}\n\nfunction summarize(\n entity: string,\n name: string,\n setting: SchemaSetting,\n saved: { found: boolean; value: unknown },\n): SettingSummary {\n return {\n key: `${entity}.${name}`,\n label: setting.label ?? null,\n description: setting.description ?? null,\n type: setting.type ?? null,\n value: saved.found ? saved.value : (setting.value ?? null),\n default: setting.value ?? null,\n ...(setting.options ? { options: setting.options.map((o) => o.value) } : {}),\n ...(setting.multiple !== undefined ? { multiple: setting.multiple } : {}),\n ...(setting.depends_on ? { dependsOn: setting.depends_on } : {}),\n };\n}\n\nfunction savedValue(\n saved: Record<string, unknown>,\n entity: string,\n name: string,\n): { found: boolean; value: unknown } {\n const entry =\n entity === \"sponsor_tree\" && name === \"sponsor_tree\"\n ? saved[\"sponsor_tree\"]\n : dig(saved, [\"entities\", entity, \"settings\", name]);\n if (entry && typeof entry === \"object\" && \"value\" in entry) {\n return { found: true, value: (entry as { value: unknown }).value };\n }\n return { found: false, value: undefined };\n}\n\nfunction dig(node: unknown, path: string[]): unknown {\n let current = node;\n for (const key of path) {\n if (!current || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[key];\n }\n return current;\n}\n","import {\n mappingType,\n createMapping,\n getCompanyIntegration,\n mappingPath,\n type ConnectClient,\n} from \"./api.js\";\nimport type { Provider } from \"./providers.js\";\n\n/** The field each mapping type keeps the Fluid (source) value in. A row\n * whose source is `*` is the fallback for every value without its own row. */\nexport const SOURCE_FIELD: Record<string, string> = {\n customer_type: \"source_type_slug\",\n customer_status: \"source_status_slug\",\n order_status: \"source_status_slug\",\n order_type: \"source_type_slug\",\n payable_type: \"source_type_slug\",\n price_type: \"source_price_type_title\",\n warehouse: \"source_warehouse_title\",\n customer_warehouse: \"source_country_code\",\n payment_type: \"source_payment_method\",\n wallet_type: \"source_payment_method\",\n fraud_decision: \"source_decision\",\n ship_method: \"source_shipping_method_title\",\n product_type: \"source_title\",\n country_market: \"source_title\",\n tree: \"source_title\",\n rank: \"source_title\",\n};\n\n/** The field each mapping type keeps the provider's (target) value in. */\nexport const TARGET_FIELD: Record<string, string> = {\n customer_type: \"target_type_id\",\n customer_status: \"target_status_id\",\n order_status: \"target_status_id\",\n order_type: \"target_type_id\",\n payable_type: \"target_payable_id\",\n price_type: \"target_price_type_id\",\n warehouse: \"external_warehouse_id\",\n customer_warehouse: \"target_warehouse_id\",\n payment_type: \"target_payment_type\",\n wallet_type: \"target_wallet_type_id\",\n fraud_decision: \"target_field_value\",\n ship_method: \"exigo_ship_method_id\",\n product_type: \"target_title\",\n country_market: \"target_title\",\n tree: \"target_title\",\n rank: \"target_title\",\n};\n\n/** Types whose Fluid values the API lists (`source_options`), with a\n * `mapped` flag on each. Ranks and ship methods have no such list. */\nconst LISTS_FLUID_VALUES = new Set([\n \"customer_type\",\n \"customer_status\",\n \"order_status\",\n \"order_type\",\n \"payable_type\",\n \"price_type\",\n \"warehouse\",\n \"customer_warehouse\",\n \"payment_type\",\n \"wallet_type\",\n \"fraud_decision\",\n \"currency_point_account\",\n \"product_type\",\n \"country_market\",\n \"tree\",\n]);\n\ninterface FluidOption {\n id?: string | number;\n title?: string;\n slug?: string;\n mapped?: boolean;\n}\n\ninterface ProviderOption {\n id?: string | number;\n title?: string;\n}\n\n/** How to build a row from a Fluid option and the Exigo option with the\n * same title. Only types whose target is an Exigo id are listed: a title\n * match then names exactly one Exigo record. */\nconst AUTO_MAP: Record<\n string,\n (fluid: FluidOption, provider: ProviderOption) => Record<string, unknown> | null\n> = {\n customer_type: (f, p) => f.slug ? { source_type_slug: f.slug, target_type_id: String(p.id) } : null,\n customer_status: (f, p) => f.slug ? { source_status_slug: f.slug, target_status_id: String(p.id) } : null,\n order_status: (f, p) => f.slug ? { source_status_slug: f.slug, target_status_id: String(p.id) } : null,\n order_type: (f, p) => f.slug ? { source_type_slug: f.slug, target_type_id: String(p.id) } : null,\n payable_type: (f, p) => f.slug ? { source_type_slug: f.slug, target_payable_id: String(p.id) } : null,\n price_type: (f, p) => f.title ? { source_price_type_title: f.title, target_price_type_id: String(p.id) } : null,\n warehouse: (f, p) =>\n f.title && f.id !== undefined\n ? { fluid_warehouse_id: f.id, source_warehouse_title: f.title, external_warehouse_id: String(p.id) }\n : null,\n};\n\nexport interface UnmappedValue {\n /** What to put in the source field when you add the row. */\n value: string;\n title: string | null;\n /** The Fluid record's id, which a warehouse row also needs. */\n id?: string | number;\n}\n\nexport interface MappingTypeReport {\n type: string;\n /** covered: every Fluid value has a row or a `*` fallback covers them.\n * needs_mapping: `unmapped` lists the values a person must map.\n * check_by_hand: the API doesn't list Fluid's values for this type.\n * error: the values couldn't be read; see `error`. */\n status: \"covered\" | \"needs_mapping\" | \"check_by_hand\" | \"error\";\n sourceField: string | null;\n fallback?: boolean;\n unmapped?: UnmappedValue[];\n autoMapped?: Array<Record<string, unknown>>;\n error?: string;\n}\n\nconst norm = (text: string | undefined): string =>\n (text ?? \"\").trim().toLowerCase();\n\n/** The value a row's source field takes for a Fluid option: the title for\n * fields keyed by title (`source_price_type_title`), else the slug. */\nexport function sourceValue(option: FluidOption, sourceField?: string | null): string {\n const value = sourceField?.endsWith(\"_title\")\n ? (option.title ?? option.slug ?? option.id)\n : (option.slug ?? option.title ?? option.id);\n return String(value ?? \"\");\n}\n\n/** Pair unmapped Fluid options with provider options whose title matches\n * exactly (ignoring case and edge spaces). A title that more than one\n * provider option shares is ambiguous and left for a person. */\nexport function exactTitleMatches(\n unmapped: FluidOption[],\n providerOptions: ProviderOption[],\n): Array<{ fluid: FluidOption; provider: ProviderOption }> {\n const byTitle = new Map<string, ProviderOption[]>();\n for (const option of providerOptions) {\n const key = norm(option.title);\n if (!key || option.id === undefined || option.id === null) continue;\n byTitle.set(key, [...(byTitle.get(key) ?? []), option]);\n }\n const pairs: Array<{ fluid: FluidOption; provider: ProviderOption }> = [];\n for (const fluid of unmapped) {\n const candidates = byTitle.get(norm(fluid.title)) ?? [];\n if (candidates.length === 1) pairs.push({ fluid, provider: candidates[0]! });\n }\n return pairs;\n}\n\nfunction optionsOf(response: unknown): unknown[] {\n const options = (response as { options?: unknown })?.options;\n return Array.isArray(options) ? options : [];\n}\n\nfunction rowsOf(response: unknown, type: string): Array<Record<string, unknown>> {\n const rows = (response as Record<string, unknown> | null)?.[`${type}_mappings`];\n return Array.isArray(rows) ? (rows as Array<Record<string, unknown>>) : [];\n}\n\nfunction errorText(error: unknown): string {\n const body = (error as { body?: Record<string, unknown> | null }).body;\n const detail = body?.[\"error\"] ?? body?.[\"errors\"];\n if (typeof detail === \"string\") return detail;\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * For every mapping type the provider supports, the Fluid values that still\n * need a row. With `autoMap`, rows are first created for values whose title\n * matches exactly one provider value (Exigo only, and only types whose target\n * is an id), so what's returned is what a person still has to decide.\n */\nexport async function unmappedReport(\n integrations: ConnectClient,\n ciId: number,\n provider: Provider,\n autoMap: boolean,\n): Promise<MappingTypeReport[]> {\n const ci = await getCompanyIntegration(integrations, ciId);\n const types = (ci.supported_mappings ?? [])\n .map(mappingType)\n .filter((type) => type in SOURCE_FIELD || LISTS_FLUID_VALUES.has(type));\n\n const reports: MappingTypeReport[] = [];\n for (const type of types) {\n reports.push(await reportFor(integrations, ciId, provider, type, autoMap));\n }\n return reports;\n}\n\nasync function reportFor(\n integrations: ConnectClient,\n ciId: number,\n provider: Provider,\n type: string,\n autoMap: boolean,\n): Promise<MappingTypeReport> {\n const sourceField = SOURCE_FIELD[type] ?? null;\n if (!LISTS_FLUID_VALUES.has(type)) {\n return { type, status: \"check_by_hand\", sourceField };\n }\n // Kept outside the try so a later failure still reports what was saved.\n const autoMapped: Array<Record<string, unknown>> = [];\n try {\n const path = mappingPath(ciId, type);\n const [optionsResponse, rowsResponse] = await Promise.all([\n integrations.get(`${path}/source_options`),\n integrations.get(path),\n ]);\n const fallback =\n sourceField !== null &&\n rowsOf(rowsResponse, type).some((row) => row[sourceField] === \"*\");\n if (fallback) return { type, status: \"covered\", sourceField, fallback: true };\n\n let unmapped = (optionsOf(optionsResponse) as FluidOption[]).filter(\n (option) => option.mapped === false,\n );\n const build = AUTO_MAP[type];\n if (autoMap && provider.targetOptions && build && unmapped.length > 0) {\n const providerOptions = optionsOf(\n await integrations.get(`${path}/exigo_options`),\n ) as ProviderOption[];\n for (const { fluid, provider: match } of exactTitleMatches(unmapped, providerOptions)) {\n const fields = build(fluid, match);\n if (!fields) continue;\n await createMapping(integrations, ciId, type, fields);\n autoMapped.push(fields);\n unmapped = unmapped.filter((option) => option !== fluid);\n }\n }\n return {\n type,\n status: unmapped.length === 0 ? \"covered\" : \"needs_mapping\",\n sourceField,\n unmapped: unmapped.map((option) => ({\n value: sourceValue(option, sourceField),\n title: option.title ?? null,\n ...(option.id !== undefined && option.id !== null ? { id: option.id } : {}),\n })),\n ...(autoMapped.length > 0 ? { autoMapped } : {}),\n };\n } catch (error) {\n return {\n type,\n status: \"error\",\n sourceField,\n error: errorText(error),\n ...(autoMapped.length > 0 ? { autoMapped } : {}),\n };\n }\n}\n","// `fluid connect walk <provider>`: the setup guide as a conversation. It\n// installs the provider if needed, asks for the credentials, maps each\n// unmapped value, and goes through the common sync settings. Every answer\n// is saved as it's given, through the same functions the non-interactive\n// commands use, and a person can go back, skip, or open a menu to jump to a\n// part or change an answer.\n\nimport {\n NAV_HELP,\n type Choice,\n type Nav,\n type Prompter,\n type Question,\n} from \"@fluid-app/fluid-cli\";\n\nimport {\n createMapping,\n deleteMapping,\n getCompanyIntegration,\n installProvider,\n mappingPath,\n providerStatus,\n saveCredentials,\n updateSyncSettings,\n type CompanyIntegrationWire,\n type ConnectClient,\n type SchemaSetting,\n} from \"./api.js\";\nimport {\n ConnectError,\n type CredentialField,\n type Provider,\n type ProviderId,\n} from \"./providers.js\";\nimport { COMMON_SETTINGS, defaultsPatch, settingsPatch } from \"./settings.js\";\nimport { summarizeSettings } from \"./summary.js\";\nimport {\n SOURCE_FIELD,\n TARGET_FIELD,\n unmappedReport,\n type MappingTypeReport,\n type UnmappedValue,\n} from \"./unmapped.js\";\n\nexport interface WalkDeps {\n /** The Fluid API, for installing the droplet. */\n fluid: ConnectClient;\n /** fluid-integrations, where the connection lives. */\n integrations: ConnectClient;\n provider: Provider & { id: ProviderId };\n prompter: Prompter;\n /** Waits between checks for a just-installed connection. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface WalkResult {\n provider: ProviderId;\n installed: boolean;\n connected: boolean;\n /** True when the walk saved credentials that passed the connection test. */\n credentialsSaved: boolean;\n /** Rows added by matching names (Exigo). */\n autoMapped: number;\n /** Rows added from answers, including `*` fallbacks. */\n rowsAdded: number;\n /** Every mapping type is covered. Null when it couldn't be checked. */\n mappingsDone: boolean | null;\n /** Types that still have values without a row, with how many. */\n stillUnmapped: Array<{ type: string; count: number | null; status: string }>;\n settingsChanged: string[];\n settingsRestored: boolean;\n /** The questions passed over with `>`. */\n skipped: string[];\n /** Stopped before the end (Ctrl-C or Stop in the menu). */\n stopped: boolean;\n}\n\n// ---------------------------------------------------------------- steps\n\ntype Phase = \"credentials\" | \"mappings\" | \"settings\";\n\nconst PHASE_NAMES: Record<Phase, string> = {\n credentials: \"Credentials\",\n mappings: \"Mappings\",\n settings: \"Settings\",\n};\n\n/** What a step did: answered, asked to move, or wants to go elsewhere. */\ntype Outcome =\n | { done: true; answer?: string }\n | { nav: Nav }\n | { retry: true }\n | { goto: string }\n | { stop: true };\n\ninterface Step {\n id: string;\n phase: Phase;\n /** Steps generated together (one report, one settings pass) share a group\n * so they can be replaced as a whole. */\n group?: string;\n /** How the menu names it. */\n title: string;\n /** False for steps that only act (save, load), which back passes over. */\n asks: boolean;\n when?: () => boolean;\n run(canGoBack: boolean): Promise<Outcome>;\n}\n\nconst DONE: Outcome = { done: true };\nconst FALLBACK = \"*\";\n\n/** The reason a request failed: the provider's, on a failed connection test. */\nexport function reasonOf(error: unknown): string {\n const body = (error as { body?: Record<string, unknown> | null }).body;\n const detail = body?.[\"errors\"] ?? body?.[\"error\"] ?? body?.[\"error_message\"];\n if (typeof detail === \"string\") return detail;\n if (Array.isArray(detail)) return detail.map(String).join(\"; \");\n if (detail && typeof detail === \"object\") {\n return Object.entries(detail)\n .map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(\", \") : String(value)}`)\n .join(\"; \");\n }\n return error instanceof Error ? error.message : String(error);\n}\n\nconst words = (type: string) => type.replace(/_/g, \" \");\n\nfunction display(value: unknown): string {\n if (value === undefined || value === null || value === \"\") return \"(none)\";\n if (typeof value === \"boolean\") return value ? \"Yes\" : \"No\";\n return typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\nfunction setPath(target: Record<string, unknown>, key: string, value: string): void {\n const path = key.split(\".\");\n let node = target;\n for (const part of path.slice(0, -1)) {\n if (typeof node[part] !== \"object\" || node[part] === null) node[part] = {};\n node = node[part] as Record<string, unknown>;\n }\n node[path[path.length - 1]!] = value;\n}\n\nfunction rowIdOf(response: unknown, type: string): number | undefined {\n const record = response as Record<string, unknown> | null;\n const row = (record?.[`${type}_mapping`] ?? record) as { id?: unknown } | null;\n return typeof row?.id === \"number\" ? row.id : undefined;\n}\n\n// ----------------------------------------------------------------- walk\n\nexport async function walkConnect(deps: WalkDeps): Promise<WalkResult> {\n const { integrations, provider, prompter } = deps;\n const sleep = deps.sleep ?? ((ms: number) => new Promise((r) => setTimeout(r, ms)));\n const label = provider.label;\n\n const result: WalkResult = {\n provider: provider.id,\n installed: false,\n connected: false,\n credentialsSaved: false,\n autoMapped: 0,\n rowsAdded: 0,\n mappingsDone: null,\n stillUnmapped: [],\n settingsChanged: [],\n settingsRestored: false,\n skipped: [],\n stopped: false,\n };\n\n prompter.note(NAV_HELP);\n\n // ------------------------------------------------------------ install\n let status = await providerStatus(integrations, provider);\n if (!status.installed) {\n const choice = await prompter.menu(\n `${label} isn't installed for this company. Installing it adds the ${label} droplet in Fluid, which creates the connection (changes live config).`,\n [\n { title: `Install ${label}`, value: \"install\" },\n { title: \"Stop\", value: \"stop\" },\n ],\n );\n if (choice !== \"install\") {\n result.stopped = true;\n return result;\n }\n await installProvider(deps.fluid, provider, status.dropletUuid);\n prompter.note(`Installed ${label}. Waiting for the connection…`);\n for (let attempt = 0; attempt < 10 && !status.installed; attempt += 1) {\n await sleep(2000);\n status = await providerStatus(integrations, provider);\n }\n if (!status.installed) {\n prompter.note(`The connection isn't there yet, so saving the credentials will create it.`);\n }\n }\n result.installed = true;\n result.connected = status.connected;\n /** Null until the connection exists; saving credentials creates it. */\n let ciId: number | null = status.companyIntegrationId;\n let ci: CompanyIntegrationWire =\n ciId === null ? { id: 0 } : await getCompanyIntegration(integrations, ciId);\n /** The connection, for the parts that need one (guarded at mappings-start). */\n const connection = (): number => {\n if (ciId === null) throw new ConnectError(`${label} has no connection yet. Save its credentials first.`);\n return ciId;\n };\n\n // --------------------------------------------------------- the walk\n const steps: Step[] = [];\n const history: string[] = [];\n const skipped = new Map<string, string>();\n const answers = new Map<string, string>();\n let index = 0;\n /** The step reached by back or the menu: asked even if answered. */\n let targeted: string | undefined;\n\n const indexOf = (id: string) => steps.findIndex((s) => s.id === id);\n\n /** Put `fresh` in place of the group's steps, right after `afterId`. */\n const replaceGroup = (group: string, afterId: string, fresh: Step[]) => {\n for (let i = steps.length - 1; i >= 0; i -= 1) {\n if (steps[i]!.group === group) {\n answers.delete(steps[i]!.id);\n skipped.delete(steps[i]!.id);\n steps.splice(i, 1);\n }\n }\n steps.splice(indexOf(afterId) + 1, 0, ...fresh);\n };\n\n const ask = (question: Omit<Question, \"canGoBack\">, canGoBack: boolean) =>\n prompter.ask({ ...question, canGoBack });\n\n // ------------------------------------------------------ credentials\n const draft = new Map<string, string>();\n const credentialSteps: Step[] = [];\n const firstCredential = () => credentialSteps.find((s) => s.asks && s.id !== \"credentials-keep\")!.id;\n\n if (status.connected) {\n credentialSteps.push({\n id: \"credentials-keep\",\n phase: \"credentials\",\n title: \"Change the credentials?\",\n asks: true,\n run: async (canGoBack) => {\n const reply = await ask(\n {\n section: PHASE_NAMES.credentials,\n message: `${label} is connected. Change its credentials?`,\n kind: \"select\",\n choices: [\n { title: \"Keep the saved credentials\", value: \"keep\" },\n { title: \"Change them\", value: \"change\" },\n ],\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n if (reply.value === \"keep\") {\n answers.set(\"credentials-keep\", \"Keep\");\n history.push(\"credentials-keep\");\n return { goto: \"mappings-start\" };\n }\n return { done: true, answer: \"Change\" };\n },\n });\n }\n\n const required = (field: CredentialField) => !field.optional && !result.connected;\n\n const credentialStep = (field: CredentialField, when?: () => boolean): Step => ({\n id: `credential:${field.key}`,\n phase: \"credentials\",\n title: field.label,\n asks: true,\n ...(when ? { when } : {}),\n run: async (canGoBack) => {\n const typed = draft.get(field.key);\n const reply = await ask(\n {\n section: `${PHASE_NAMES.credentials} · ${field.key}`,\n message: field.label,\n kind: field.secret ? \"secret\" : \"text\",\n ...(typed !== undefined ? { current: field.secret ? \"••••••\" : typed } : {}),\n ...(result.connected\n ? { hint: \"Enter keeps the saved value\" }\n : field.optional\n ? { hint: \"optional; Enter leaves it blank\" }\n : {}),\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n const value = reply.value.trim();\n if (value === \"\") {\n if (typed !== undefined || !required(field)) return { done: true, answer: typed !== undefined ? \"(entered)\" : \"(unchanged)\" };\n prompter.note(`${label} needs this. Type it, or > to skip.`);\n return { retry: true };\n }\n draft.set(field.key, value);\n return { done: true, answer: field.secret ? \"(entered)\" : value };\n },\n });\n\n if (provider.environments) {\n credentialSteps.push({\n id: \"credential:environment\",\n phase: \"credentials\",\n title: `${label} environment`,\n asks: true,\n run: async (canGoBack) => {\n const reply = await ask(\n {\n section: `${PHASE_NAMES.credentials} · environment`,\n message: `Which ${label} environment?`,\n kind: \"select\",\n choices: [\n { title: \"Production (your live Exigo)\", value: \"production\" },\n { title: \"Sandbox\", value: \"sandbox\" },\n ],\n ...(draft.get(\"environment\") ?? ci.environment\n ? { current: draft.get(\"environment\") ?? ci.environment ?? undefined }\n : {}),\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n const environment = reply.value.trim() || draft.get(\"environment\") || ci.environment;\n if (!environment) {\n prompter.note(\"Choose an environment, or > to skip.\");\n return { retry: true };\n }\n draft.set(\"environment\", environment);\n return { done: true, answer: environment };\n },\n });\n credentialSteps.push(\n credentialStep(\n { key: \"sandbox_id\", label: \"Exigo sandbox ID (the number in sandboxapi<ID>.exigo.com)\" },\n () => (draft.get(\"environment\") ?? ci.environment) !== \"production\",\n ),\n );\n }\n for (const field of provider.credentials) credentialSteps.push(credentialStep(field));\n\n credentialSteps.push({\n id: \"credentials-save\",\n phase: \"credentials\",\n title: \"Save the credentials\",\n asks: false,\n run: async () => {\n if (draft.size === 0) return DONE;\n const authSettings: Record<string, unknown> = {};\n for (const [key, value] of draft) setPath(authSettings, key, value);\n prompter.note(`Testing the connection to ${label}…`);\n try {\n ciId = (await saveCredentials(integrations, deps.fluid, provider, authSettings)).companyIntegrationId;\n } catch (error) {\n prompter.note(`${label} didn't accept the credentials, so nothing was saved: ${reasonOf(error)}`);\n prompter.note(\"Check the values and answer again. Enter keeps what you typed.\");\n for (const step of credentialSteps) answers.delete(step.id);\n return { goto: firstCredential() };\n }\n draft.clear();\n result.credentialsSaved = true;\n result.connected = true;\n prompter.note(\"Connected. The credentials are saved.\");\n return DONE;\n },\n });\n steps.push(...credentialSteps);\n\n // --------------------------------------------------------- mappings\n let autoMap = false;\n const providerChoices = new Map<string, Choice[]>();\n /** Types a `*` row now covers, so their other values aren't asked. */\n const fallbackTypes = new Set<string>();\n /** The row each mapping answer created, replaced when it's changed. */\n const rowsByStep = new Map<string, { type: string; id?: number; fallback: boolean }>();\n\n steps.push({\n id: \"mappings-start\",\n phase: \"mappings\",\n title: \"Mappings\",\n asks: false,\n run: async () => {\n if (ciId !== null) return DONE;\n prompter.note(\n `Mappings and settings need the connection, which saving ${label}'s credentials creates. Answer the credentials, or run \\`fluid connect credentials ${provider.id}\\`.`,\n );\n return { stop: true };\n },\n });\n\n if (provider.targetOptions) {\n steps.push({\n id: \"mappings-automap\",\n phase: \"mappings\",\n title: \"Map exact name matches\",\n asks: true,\n run: async (canGoBack) => {\n const reply = await ask(\n {\n section: PHASE_NAMES.mappings,\n message: `Add a mapping wherever a Fluid value's name matches exactly one ${label} value? (changes live config)`,\n kind: \"select\",\n choices: [\n { title: \"Yes, map exact name matches first\", value: \"yes\" },\n { title: \"No, I'll map each one\", value: \"no\" },\n ],\n ...(answers.has(\"mappings-automap\") ? { current: autoMap ? \"yes\" : \"no\" } : {}),\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n autoMap = reply.value === \"yes\";\n return { done: true, answer: autoMap ? \"Yes\" : \"No\" };\n },\n });\n }\n\n /** The provider's values for a type, as pick-list choices (Exigo). */\n const choicesFor = async (type: string): Promise<Choice[]> => {\n if (!provider.targetOptions) return [];\n const cached = providerChoices.get(type);\n if (cached) return cached;\n let choices: Choice[] = [];\n try {\n const response = await integrations.get<{ options?: Array<{ id?: unknown; title?: string }> }>(\n `${mappingPath(connection(), type)}/exigo_options`,\n );\n const byTitle = (TARGET_FIELD[type] ?? \"\").endsWith(\"_title\");\n choices = (response?.options ?? [])\n .filter((o) => o.id !== undefined && o.id !== null)\n .map((o) => ({\n title: o.title ? `${o.title} (${String(o.id)})` : String(o.id),\n value: byTitle && o.title ? o.title : String(o.id),\n }));\n } catch {\n choices = []; // fall back to typing the value\n }\n providerChoices.set(type, choices);\n return choices;\n };\n\n /** Ask for the provider's value: a pick list for Exigo, typed otherwise. */\n const askTarget = async (\n type: string,\n message: string,\n section: string,\n canGoBack: boolean,\n offerFallback: boolean,\n current?: string,\n ) => {\n const choices = await choicesFor(type);\n const fallback: Choice[] = offerFallback\n ? [{ title: `* Use one ${label} value for every remaining ${words(type)}`, value: FALLBACK }]\n : [];\n if (choices.length > 0) {\n return ask(\n {\n section,\n message,\n kind: \"search\",\n choices: [...choices, ...fallback],\n hint: \"type to filter\",\n ...(current ? { current } : {}),\n },\n canGoBack,\n );\n }\n const targetIsName = (TARGET_FIELD[type] ?? \"\").endsWith(\"_title\");\n return ask(\n {\n section,\n message,\n kind: \"text\",\n hint: [\n `${label}'s ${words(type)} ${targetIsName ? \"name\" : \"ID\"}, from your ${label} back office`,\n offerFallback ? `or * to use one value for every remaining ${words(type)}` : \"\",\n ]\n .filter(Boolean)\n .join(\"; \"),\n ...(current ? { current } : {}),\n },\n canGoBack,\n );\n };\n\n /** Save one row, replacing the row this step saved before. */\n const saveRow = async (\n stepId: string,\n type: string,\n fields: Record<string, unknown>,\n fallback: boolean,\n ): Promise<boolean> => {\n let response: unknown;\n try {\n response = await createMapping(integrations, connection(), type, fields);\n } catch (error) {\n prompter.note(`Not saved: ${reasonOf(error)}`);\n return false;\n }\n const previous = rowsByStep.get(stepId);\n if (previous) {\n if (previous.fallback) fallbackTypes.delete(previous.type);\n if (previous.id !== undefined) {\n try {\n await deleteMapping(integrations, connection(), previous.type, previous.id);\n } catch (error) {\n prompter.note(\n `The earlier row (${previous.id}) is still there: ${reasonOf(error)}. Remove it with \\`fluid connect unmap ${provider.id} ${previous.type} ${previous.id} --yes\\`.`,\n );\n }\n }\n } else {\n result.rowsAdded += 1;\n }\n const id = rowIdOf(response, type);\n rowsByStep.set(stepId, { type, ...(id !== undefined ? { id } : {}), fallback });\n if (fallback) fallbackTypes.add(type);\n return true;\n };\n\n const valueStep = (\n report: MappingTypeReport,\n value: UnmappedValue,\n position: number,\n ): Step => {\n const type = report.type;\n const id = `map:${type}:${value.value}`;\n const name = value.title && value.title !== value.value ? `\"${value.title}\" (${value.value})` : `\"${value.value}\"`;\n const section = `${PHASE_NAMES.mappings} · ${words(type)} · ${position} of ${report.unmapped!.length}`;\n return {\n id,\n phase: \"mappings\",\n group: \"mapping-values\",\n title: `${words(type)}: ${value.title ?? value.value}`,\n asks: true,\n // A `*` row saved by an earlier answer covers the rest of the type.\n when: () => !fallbackTypes.has(type) || rowsByStep.get(id)?.fallback === true,\n run: async (canGoBack) => {\n const reply = await askTarget(\n type,\n `${label} ${words(type)} for Fluid's ${name}`,\n section,\n canGoBack,\n true,\n answers.get(id),\n );\n if (\"nav\" in reply) return reply;\n let target = reply.value.trim();\n if (target === \"\") {\n if (answers.has(id)) return { done: true, answer: answers.get(id)! };\n prompter.note(\"Choose a value, or > to skip.\");\n return { retry: true };\n }\n if (target === answers.get(id)) return { done: true, answer: target };\n const fallback = target === FALLBACK;\n if (fallback) {\n const second = await askTarget(\n type,\n `${label} ${words(type)} for every Fluid ${words(type)} without its own row`,\n section,\n canGoBack,\n false,\n );\n if (\"nav\" in second) return second.nav === \"back\" ? { retry: true } : second;\n target = second.value.trim();\n if (target === \"\") return { retry: true };\n }\n const sourceField = SOURCE_FIELD[type]!;\n const fields: Record<string, unknown> = {\n // value.value is already the title for title-keyed fields.\n [sourceField]: fallback ? FALLBACK : value.value,\n [TARGET_FIELD[type]!]: target,\n };\n if (type === \"warehouse\" && !fallback && value.id !== undefined) {\n fields[\"fluid_warehouse_id\"] = value.id;\n fields[\"source_warehouse_title\"] = value.title ?? value.value;\n }\n if (!(await saveRow(id, type, fields, fallback))) return { retry: true };\n const answer = fallback ? `* → ${target}` : target;\n prompter.note(\n fallback\n ? `Saved a * row: every other ${words(type)} maps to ${target}.`\n : `Saved: ${value.title ?? value.value} → ${target}.`,\n );\n return { done: true, answer };\n },\n };\n };\n\n /** Ranks and ship methods: the API can't list Fluid's values. */\n const byHandStep = (report: MappingTypeReport): Step => {\n const type = report.type;\n const id = `by-hand:${type}`;\n return {\n id,\n phase: \"mappings\",\n group: \"mapping-values\",\n title: `${words(type)} (by hand)`,\n asks: true,\n run: async (canGoBack) => {\n const section = `${PHASE_NAMES.mappings} · ${words(type)}`;\n const reply = await ask(\n {\n section,\n message: `Fluid can't list its ${words(type)}s, so compare \\`fluid connect mappings ${provider.id} ${type}\\` with them yourself. Add a ${words(type)} row now?`,\n kind: \"select\",\n choices: [\n { title: \"Not now\", value: \"no\" },\n { title: `Add a ${words(type)} row`, value: \"add\" },\n ],\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n if (reply.value !== \"add\") return { done: true, answer: answers.get(id) ?? \"Not now\" };\n\n const sourceField = report.sourceField ?? SOURCE_FIELD[type]!;\n const source = await ask(\n { section, message: `Fluid ${words(type)} name (or * for every one without its own row)`, kind: \"text\" },\n true,\n );\n if (\"nav\" in source) return source.nav === \"back\" ? { retry: true } : source;\n if (source.value.trim() === \"\") return { retry: true };\n const target = await askTarget(type, `${label} ${words(type)} for \"${source.value.trim()}\"`, section, true, false);\n if (\"nav\" in target) return target.nav === \"back\" ? { retry: true } : target;\n if (target.value.trim() === \"\") return { retry: true };\n const fields: Record<string, unknown> = {\n [sourceField]: source.value.trim(),\n [TARGET_FIELD[type]!]: target.value.trim(),\n };\n if (type === \"ship_method\") {\n const country = await ask(\n { section, message: \"Country code for this ship method (Enter for any country)\", kind: \"text\" },\n true,\n );\n if (\"nav\" in country) return country.nav === \"back\" ? { retry: true } : country;\n if (country.value.trim() !== \"\") fields[\"country_code\"] = country.value.trim().toUpperCase();\n }\n try {\n await createMapping(integrations, connection(), type, fields);\n } catch (error) {\n prompter.note(`Not saved: ${reasonOf(error)}`);\n return { retry: true };\n }\n result.rowsAdded += 1;\n const added = Number(answers.get(`${id}#count`) ?? \"0\") + 1;\n answers.set(`${id}#count`, String(added));\n answers.set(id, `${added} row(s) added`);\n prompter.note(`Saved: ${source.value.trim()} → ${target.value.trim()}.`);\n return { retry: true }; // ask again: another row, or Not now\n },\n };\n };\n\n steps.push({\n id: \"mappings-report\",\n phase: \"mappings\",\n title: \"Find what needs mapping\",\n asks: false,\n run: async () => {\n prompter.note(autoMap ? \"Mapping exact name matches, then checking what's left…\" : \"Checking what needs mapping…\");\n const report = await unmappedReport(integrations, connection(), provider, autoMap);\n const fresh: Step[] = [];\n let added = 0;\n for (const entry of report) {\n added += entry.autoMapped?.length ?? 0;\n if (entry.status === \"needs_mapping\" && entry.sourceField && TARGET_FIELD[entry.type]) {\n entry.unmapped!.forEach((value, i) => fresh.push(valueStep(entry, value, i + 1)));\n } else if (entry.status === \"needs_mapping\") {\n prompter.note(\n `${words(entry.type)}: ${entry.unmapped!.length} value(s) to map. Add them with \\`fluid connect map ${provider.id} ${entry.type} --set field=value --yes\\`.`,\n );\n } else if (entry.status === \"check_by_hand\" && TARGET_FIELD[entry.type]) {\n fresh.push(byHandStep(entry));\n } else if (entry.status === \"error\") {\n prompter.note(`${words(entry.type)}: couldn't read its values (${entry.error}). Check it later with \\`fluid connect unmapped ${provider.id}\\`.`);\n }\n }\n if (added > 0) {\n result.autoMapped += added;\n prompter.note(`Added ${added} row(s) by matching names.`);\n }\n const toMap = fresh.filter((s) => s.id.startsWith(\"map:\")).length;\n prompter.note(\n toMap === 0\n ? \"Every value Fluid lists is mapped.\"\n : `${toMap} value(s) to map. Pick the ${label} value for each; * covers every remaining value of that type.`,\n );\n replaceGroup(\"mapping-values\", \"mappings-report\", fresh);\n return DONE;\n },\n });\n\n // --------------------------------------------------------- settings\n const settingStep = (key: string, setting: SchemaSetting, position: number, total: number): Step => {\n const id = `setting:${key}`;\n return {\n id,\n phase: \"settings\",\n group: \"settings-values\",\n title: setting.label ?? key,\n asks: true,\n run: async (canGoBack) => {\n const summary = summarizeSettings(ci).find((s) => s.key === key);\n const current = summary ? summary.value : setting.value;\n const fallbackLabel = (value: unknown) =>\n setting.options?.find((o) => o.value === value)?.label ?? display(value);\n const question: Omit<Question, \"canGoBack\"> = {\n section: `${PHASE_NAMES.settings} · ${position} of ${total} · ${key}`,\n message: `${setting.label ?? key} (default: ${fallbackLabel(setting.value)})`,\n kind: \"text\",\n ...(setting.description ? { hint: setting.description } : {}),\n };\n let choices: Array<Choice & { raw: unknown }> | undefined;\n if (setting.options && setting.options.length > 0 && !setting.multiple) {\n choices = setting.options.map((o, i) => ({\n title: `${o.label ?? display(o.value)}${o.value === setting.value ? \" (default)\" : \"\"}`,\n value: String(i),\n raw: o.value,\n }));\n } else if (setting.type === \"boolean\") {\n choices = [\n { title: `Yes${setting.value === true ? \" (default)\" : \"\"}`, value: \"yes\", raw: true },\n { title: `No${setting.value === false ? \" (default)\" : \"\"}`, value: \"no\", raw: false },\n ];\n }\n if (choices) {\n question.kind = \"select\";\n question.choices = choices.map(({ raw: _raw, ...c }) => c);\n const now = choices.find((c) => c.raw === current);\n if (now) question.current = now.title;\n } else {\n // Nothing saved: no pre-fill, so Enter keeps it unset.\n if (current !== null && current !== undefined && current !== \"\") {\n question.current = display(current);\n }\n question.hint = [setting.description, \"JSON for lists and objects\"].filter(Boolean).join(\" · \");\n }\n\n const reply = await ask(question, canGoBack);\n if (\"nav\" in reply) return reply;\n let value: unknown;\n if (choices) {\n value = choices.find((c) => c.value === reply.value)?.raw;\n } else {\n const raw = reply.value.trim();\n if (raw === \"\") return { done: true, answer: display(current) };\n try {\n value = /^(true|false|null|-?\\d+(\\.\\d+)?)$|^[[{]/.test(raw) ? JSON.parse(raw) : raw;\n } catch {\n value = raw;\n }\n }\n if (JSON.stringify(value) === JSON.stringify(current)) {\n return { done: true, answer: fallbackLabel(value) };\n }\n try {\n ci = await updateSyncSettings(integrations, connection(), settingsPatch({ [key]: value }));\n } catch (error) {\n prompter.note(`Not saved: ${reasonOf(error)}`);\n return { retry: true };\n }\n if (!result.settingsChanged.includes(key)) result.settingsChanged.push(key);\n prompter.note(`Saved: ${setting.label ?? key} → ${fallbackLabel(value)}.`);\n return { done: true, answer: fallbackLabel(value) };\n },\n };\n };\n\n steps.push({\n id: \"settings-choice\",\n phase: \"settings\",\n title: \"Sync settings\",\n asks: true,\n run: async (canGoBack) => {\n ci = await getCompanyIntegration(integrations, connection());\n const schema = ci.settings_schema ?? {};\n const common = COMMON_SETTINGS.flatMap((key) => {\n const [entity, name] = key.split(\".\") as [string, string];\n const setting = schema[entity]?.[name];\n return setting ? [{ key, setting }] : [];\n });\n const reply = await ask(\n {\n section: PHASE_NAMES.settings,\n message: `Sync settings. A new connection starts on the defaults, and each default is safe.`,\n kind: \"select\",\n choices: [\n { title: \"Keep every setting as it is\", value: \"keep\" },\n { title: `Go through the ${common.length} common settings`, value: \"review\" },\n { title: \"Put every setting back to its default\", value: \"defaults\" },\n ],\n },\n canGoBack,\n );\n if (\"nav\" in reply) return reply;\n if (reply.value === \"review\") {\n replaceGroup(\n \"settings-values\",\n \"settings-choice\",\n common.map(({ key, setting }, i) => settingStep(key, setting, i + 1, common.length)),\n );\n return { done: true, answer: \"Go through the common settings\" };\n }\n replaceGroup(\"settings-values\", \"settings-choice\", []);\n if (reply.value === \"defaults\") {\n try {\n ci = await updateSyncSettings(integrations, connection(), defaultsPatch(schema));\n } catch (error) {\n prompter.note(`Not saved: ${reasonOf(error)}`);\n return { retry: true };\n }\n result.settingsRestored = true;\n prompter.note(\"Every setting is back on its default.\");\n return { done: true, answer: \"Put back to defaults\" };\n }\n return { done: true, answer: \"Keep as is\" };\n },\n });\n\n // ------------------------------------------------------------- menu\n const goBack = (): boolean => {\n let previous = history.pop();\n // A regenerated report or settings pass may have removed it.\n while (previous !== undefined && indexOf(previous) < 0) previous = history.pop();\n if (previous === undefined) {\n prompter.note(\"This is the first question.\");\n return false;\n }\n skipped.delete(previous);\n index = indexOf(previous);\n targeted = previous;\n return true;\n };\n\n /** True when the walk should stop. */\n const openMenu = async (): Promise<boolean> => {\n const phases = (Object.keys(PHASE_NAMES) as Phase[]).filter((phase) =>\n steps.some((s) => s.phase === phase),\n );\n const answered = steps.filter((s) => s.asks && answers.has(s.id));\n const action = await prompter.menu(\"What next?\", [\n { title: \"Continue\", value: \"continue\" },\n { title: \"Back to the previous question\", value: \"back\", disabled: history.length === 0 },\n ...phases.map((phase) => ({ title: `Go to ${PHASE_NAMES[phase]}`, value: `phase:${phase}` })),\n { title: \"Change an answer\", value: \"change\", disabled: answered.length === 0 },\n { title: \"Finish: show the summary\", value: \"finish\" },\n { title: \"Stop for now (answers are saved)\", value: \"quit\" },\n ]);\n if (action === null || action === \"quit\") return true;\n if (action === \"back\") {\n goBack();\n } else if (action === \"finish\") {\n index = steps.length;\n } else if (action.startsWith(\"phase:\")) {\n const phase = action.slice(\"phase:\".length) as Phase;\n const first = phase === \"mappings\" ? \"mappings-start\" : steps.find((s) => s.phase === phase)!.id;\n index = indexOf(first);\n targeted = steps.find((s, i) => i >= index && s.asks)?.id;\n } else if (action === \"change\") {\n const id = await prompter.menu(\n \"Which answer?\",\n answered.map((s) => ({ title: `${s.title} — ${answers.get(s.id)}`, value: s.id })),\n );\n if (id) {\n index = indexOf(id);\n targeted = id;\n }\n }\n return false;\n };\n\n // ------------------------------------------------------------- loop\n while (index < steps.length) {\n const step = steps[index]!;\n if (step.when && !step.when()) {\n index += 1;\n continue;\n }\n if (step.asks && answers.has(step.id) && targeted !== step.id) {\n index += 1; // answered: walking forward passes it over\n continue;\n }\n const outcome = await step.run(history.length > 0);\n\n if (\"stop\" in outcome) {\n result.stopped = true;\n break;\n }\n if (\"goto\" in outcome) {\n index = indexOf(outcome.goto);\n targeted = steps[index]?.id;\n continue;\n }\n if (\"retry\" in outcome) {\n targeted = step.id;\n continue;\n }\n if (\"nav\" in outcome) {\n if (outcome.nav === \"quit\") {\n result.stopped = true;\n break;\n }\n if (outcome.nav === \"back\") {\n if (!goBack()) targeted = step.id;\n continue;\n }\n if (outcome.nav === \"skip\") {\n skipped.set(step.id, step.title);\n history.push(step.id);\n index += 1;\n continue;\n }\n targeted = step.id; // menu: come back here unless it moves elsewhere\n if (await openMenu()) {\n result.stopped = true;\n break;\n }\n continue;\n }\n if (step.asks) {\n history.push(step.id);\n skipped.delete(step.id);\n answers.set(step.id, outcome.answer ?? \"\");\n }\n targeted = undefined;\n index += 1;\n }\n\n // ---------------------------------------------------------- summary\n status = await providerStatus(integrations, provider);\n result.connected = status.connected;\n result.skipped = [...skipped.values()];\n try {\n if (ciId === null) throw new ConnectError(\"there's no connection yet\");\n const report = await unmappedReport(integrations, ciId, provider, false);\n result.mappingsDone = report.every((r) => r.status === \"covered\");\n result.stillUnmapped = report\n .filter((r) => r.status !== \"covered\")\n .map((r) => ({ type: r.type, count: r.unmapped?.length ?? null, status: r.status }));\n } catch (error) {\n prompter.note(`Couldn't check the mappings: ${reasonOf(error)}`);\n }\n\n const lines = [\n `${label}: ${result.connected ? \"connected\" : \"not connected yet\"}.`,\n result.credentialsSaved ? \"Credentials saved and tested.\" : \"\",\n result.autoMapped + result.rowsAdded > 0\n ? `Mapping rows added: ${result.rowsAdded}${result.autoMapped > 0 ? `, plus ${result.autoMapped} by name` : \"\"}.`\n : \"\",\n result.mappingsDone\n ? \"Every mapping type is covered.\"\n : result.stillUnmapped.length > 0\n ? `Still to map or check: ${result.stillUnmapped\n .map((r) => `${words(r.type)}${r.count ? ` (${r.count})` : r.status === \"check_by_hand\" ? \" (check by hand)\" : \"\"}`)\n .join(\", \")}. See \\`fluid connect unmapped ${provider.id}\\`.`\n : \"\",\n result.settingsRestored ? \"Every sync setting is on its default.\" : \"\",\n result.settingsChanged.length > 0 ? `Settings changed: ${result.settingsChanged.join(\", \")}.` : \"\",\n result.skipped.length > 0 ? `Skipped: ${result.skipped.join(\"; \")}.` : \"\",\n `Run \\`fluid connect walk ${provider.id}\\` again to pick up where you left off.`,\n ].filter(Boolean);\n prompter.note(`\\n${lines.join(\"\\n\")}`);\n return result;\n}\n","import type { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport type { CommandContext, FluidPlugin } from \"@fluid-app/fluid-cli\";\nimport {\n assertInteractive,\n createDomainCommand,\n terminalPrompter,\n} from \"@fluid-app/fluid-cli\";\n\nimport {\n companyIntegrationId,\n createMapping,\n deleteMapping,\n getCompanyIntegration,\n installProvider,\n integrationsBase,\n listMappings,\n mappingType,\n mappingOptions,\n providerStatus,\n saveCredentials,\n updateSyncSettings,\n type ConnectClient,\n} from \"./api.js\";\nimport { ConnectError, PROVIDER_IDS, providerOf } from \"./providers.js\";\nimport { defaultsPatch, parseAssignments, syncSettingsPatch } from \"./settings.js\";\nimport { summarizeSettings } from \"./summary.js\";\nimport { unmappedReport } from \"./unmapped.js\";\nimport { walkConnect } from \"./walk.js\";\n\nasync function fluidClient(ctx: CommandContext): Promise<ConnectClient> {\n return (await ctx.getClient()) as unknown as ConnectClient;\n}\n\nasync function integrationsClient(ctx: CommandContext): Promise<ConnectClient> {\n return (await ctx.getClientFor(integrationsBase())) as unknown as ConnectClient;\n}\n\n/**\n * Changes to a live connection need an explicit yes. Scripts and agents\n * pass `--yes`; an interactive terminal is asked. With neither, nothing runs.\n */\nexport async function confirmChange(\n question: string,\n yes: boolean,\n interactive: boolean = Boolean(process.stdin.isTTY),\n): Promise<void> {\n if (yes) return;\n if (!interactive) throw new ConnectError(`${question} Pass --yes to confirm.`);\n const answer = await prompts({\n type: \"confirm\",\n name: \"ok\",\n message: question,\n initial: false,\n });\n if (!answer.ok) throw new ConnectError(\"Cancelled.\");\n}\n\n/** Print a failure as JSON on stderr and exit non-zero. A failed connection\n * test (422) carries the provider's reason in `errors`. */\nfunction fail(err: unknown): void {\n const status =\n typeof (err as { status?: unknown }).status === \"number\"\n ? (err as { status: number }).status\n : undefined;\n const body = (err as { body?: Record<string, unknown> | null }).body;\n const detail =\n body && (body[\"errors\"] ?? body[\"error\"] ?? body[\"error_message\"]);\n const message =\n typeof detail === \"string\"\n ? detail\n : err instanceof Error\n ? err.message\n : String(err);\n process.stderr.write(\n JSON.stringify({\n error: message,\n ...(status ? { status } : {}),\n ...(detail && typeof detail === \"object\" ? { details: detail } : {}),\n }) + \"\\n\",\n );\n process.exitCode = 1;\n}\n\nfunction run(action: () => Promise<void>): Promise<void> {\n return action().catch(fail);\n}\n\nconst collect = (value: string, previous: string[] = []): string[] => [\n ...previous,\n value,\n];\n\nexport function registerConnectCommands(\n parent: Command,\n ctx: CommandContext,\n): void {\n parent\n .command(\"list\")\n .description(`Show ${PROVIDER_IDS.join(\", \")} and whether each is installed`)\n .action(() =>\n run(async () => {\n const integrations = await integrationsClient(ctx);\n ctx.output(\n await Promise.all(\n PROVIDER_IDS.map((id) => providerStatus(integrations, providerOf(id))),\n ),\n );\n }),\n );\n\n parent\n .command(\"install <provider>\")\n .description(\"Install the provider's droplet, which creates its connection (changes live config)\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, opts: { yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n const status = await providerStatus(await integrationsClient(ctx), provider);\n if (status.installed) {\n ctx.output({ ...status, alreadyInstalled: true });\n return;\n }\n await confirmChange(`Install ${provider.label} for this company?`, opts.yes);\n ctx.output({\n ...(await installProvider(await fluidClient(ctx), provider, status.dropletUuid)),\n // fluid-integrations sets up the connection from Fluid's install\n // webhook, so it can take a moment to appear.\n next: `The connection appears within a minute. Check with \\`fluid connect show ${provider.id}\\`, then save the credentials.`,\n });\n }),\n );\n\n parent\n .command(\"show <provider>\")\n .description(\"Show the connection: state, supported mappings, and every sync setting with its current value\")\n .action((providerId: string) =>\n run(async () => {\n const provider = providerOf(providerId);\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n const ci = await getCompanyIntegration(integrations, ciId);\n ctx.output({\n provider: provider.id,\n companyIntegrationId: ci.id,\n initialized: ci.initialized ?? null,\n environment: ci.environment ?? null,\n liveSyncState: ci.live_sync_state ?? null,\n liveSyncDegradedReason: ci.live_sync_degraded_reason ?? null,\n credentialGroups: provider.credentialGroups,\n // The names the mapping commands take (`customer_type`), not the\n // API's card names (`customer_type_title`).\n supportedMappings: (ci.supported_mappings ?? []).map(mappingType),\n settings: summarizeSettings(ci),\n });\n }),\n );\n\n parent\n .command(\"credentials <provider>\")\n .description(\"Save credentials; the connection is tested before anything is saved, and created if the install has not yet (changes live config). Values here land in shell history; `fluid connect walk` asks for passwords hidden\")\n .option(\"--set <key=value>\", \"A credential, e.g. api.username=alice or mssql.dataserver=sql.example.com (repeatable)\", collect, [])\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, opts: { set: string[]; yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n if (opts.set.length === 0) {\n throw new ConnectError(\n `Pass credentials with --set, e.g. --set api.username=alice. ${provider.label} uses: ${provider.credentialGroups.join(\", \")}.`,\n );\n }\n const authSettings = parseAssignments(opts.set, { strings: true });\n const integrations = await integrationsClient(ctx);\n await confirmChange(`Save new ${provider.label} credentials?`, opts.yes);\n ctx.output(await saveCredentials(integrations, await fluidClient(ctx), provider, authSettings));\n }),\n );\n\n parent\n .command(\"settings <provider>\")\n .description(\"List sync settings, change them with --set entity.key=value, or restore defaults with --defaults (changes live config)\")\n .option(\"--set <key=value>\", \"A setting from `fluid connect show`, e.g. order.refund_handling=hold (repeatable)\", collect, [])\n .option(\"--defaults\", \"Restore settings to their defaults: all of them, or only those named with --only\", false)\n .option(\"--only <key>\", \"With --defaults, restore just this setting, e.g. order.refund_handling (repeatable)\", collect, [])\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, opts: { set: string[]; defaults: boolean; only: string[]; yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n if (opts.defaults && opts.set.length > 0) {\n throw new ConnectError(\"Use --set or --defaults, not both.\");\n }\n if (opts.only.length > 0 && !opts.defaults) {\n throw new ConnectError(\"--only goes with --defaults.\");\n }\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n if (opts.defaults) {\n const ci = await getCompanyIntegration(integrations, ciId);\n const patch = defaultsPatch(ci.settings_schema ?? {}, opts.only);\n const what = opts.only.length > 0 ? `${opts.only.length} setting(s)` : \"every sync setting\";\n await confirmChange(`Restore ${what} to the default for ${provider.label}?`, opts.yes);\n ctx.output(summarizeSettings(await updateSyncSettings(integrations, ciId, patch)));\n return;\n }\n if (opts.set.length === 0) {\n ctx.output(summarizeSettings(await getCompanyIntegration(integrations, ciId)));\n return;\n }\n const patch = syncSettingsPatch(opts.set);\n await confirmChange(`Change ${opts.set.length} ${provider.label} sync setting(s)?`, opts.yes);\n ctx.output(summarizeSettings(await updateSyncSettings(integrations, ciId, patch)));\n }),\n );\n\n parent\n .command(\"walk <provider>\")\n .description(\n \"Set up the connection question by question in the terminal: install, credentials, mappings, and settings. Go back with <, skip with >, open the menu with ?; each answer is saved as you go (changes live config)\",\n )\n .action((providerId: string) =>\n run(async () => {\n assertInteractive(\n \"`install`, `credentials`, `unmapped`, `map`, and `settings` with --yes (see `fluid connect --help`)\",\n );\n const provider = providerOf(providerId);\n ctx.output(\n await walkConnect({\n fluid: await fluidClient(ctx),\n integrations: await integrationsClient(ctx),\n provider,\n prompter: terminalPrompter(),\n }),\n );\n }),\n );\n\n parent\n .command(\"unmapped <provider>\")\n .description(\"List only the data mappings a person still has to add; --auto-map first maps exact name matches (Exigo)\")\n .option(\"--auto-map\", \"Exigo: add rows where a Fluid value's name matches exactly one Exigo value (changes live config)\", false)\n .option(\"--yes\", \"Confirm --auto-map without a prompt\", false)\n .action((providerId: string, opts: { autoMap: boolean; yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n if (opts.autoMap) {\n if (!provider.targetOptions) {\n throw new ConnectError(\n `--auto-map needs ${provider.label}'s own values, which only Exigo lists. Run without it to see what to map.`,\n );\n }\n await confirmChange(`Add ${provider.label} mappings for exact name matches?`, opts.yes);\n }\n const report = await unmappedReport(integrations, ciId, provider, opts.autoMap);\n ctx.output({\n provider: provider.id,\n done: report.every((r) => r.status === \"covered\"),\n mappings: report,\n });\n // A type that couldn't be checked makes the report incomplete.\n if (report.some((r) => r.status === \"error\")) process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"mappings <provider> <type>\")\n .description(\"List one mapping type's rows (e.g. customer_type, warehouse); --options lists the values to choose from\")\n .option(\"--options\", \"List the Fluid values, and for Exigo the Exigo values, instead of the saved rows\", false)\n .action((providerId: string, type: string, opts: { options: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n ctx.output(\n opts.options\n ? await mappingOptions(integrations, ciId, type, provider)\n : await listMappings(integrations, ciId, type),\n );\n }),\n );\n\n parent\n .command(\"map <provider> <type>\")\n .description(\"Add a mapping row, e.g. --set source_type_slug=rep --set target_type_id=2 (changes live config)\")\n .option(\"--set <field=value>\", \"A field of the mapping row (repeatable)\", collect, [])\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, type: string, opts: { set: string[]; yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n if (opts.set.length === 0) {\n throw new ConnectError(\"Pass the row's fields with --set field=value.\");\n }\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n await confirmChange(`Add a ${type} mapping to ${provider.label}?`, opts.yes);\n ctx.output(await createMapping(integrations, ciId, type, parseAssignments(opts.set)));\n }),\n );\n\n parent\n .command(\"unmap <provider> <type> <id>\")\n .description(\"Remove one mapping row (changes live config)\")\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((providerId: string, type: string, id: string, opts: { yes: boolean }) =>\n run(async () => {\n const provider = providerOf(providerId);\n const rowId = Number(id);\n if (!Number.isInteger(rowId) || rowId <= 0) {\n throw new ConnectError(`\"${id}\" isn't a mapping row id.`);\n }\n const integrations = await integrationsClient(ctx);\n const ciId = await companyIntegrationId(integrations, provider);\n await confirmChange(`Remove ${type} mapping ${rowId} from ${provider.label}?`, opts.yes);\n ctx.output(await deleteMapping(integrations, ciId, type, rowId));\n }),\n );\n}\n\nconst plugin: FluidPlugin = {\n name: \"@fluid-app/fluid-cli-connect\",\n version: \"0.1.1\",\n register(ctx) {\n ctx.program.addCommand(\n createDomainCommand(\n \"connect\",\n \"Connect Exigo, InfoTrax, ByDesign, or Pillars: credentials, data mappings, and sync settings\",\n registerConnectCommands,\n ),\n );\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-app/fluid-cli-connect",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Fluid CLI plugin for Fluid Connect — connect Exigo, InfoTrax, ByDesign, or Pillars, set credentials, data mappings, and sync settings",
5
5
  "files": [
6
6
  "dist",