@native-marketing/sdk 0.3.6 → 0.3.7

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.
package/dist/index.js CHANGED
@@ -7598,6 +7598,176 @@ function capabilityStatusRead(name, input) {
7598
7598
  };
7599
7599
  }
7600
7600
 
7601
+ // packages/contracts/src/client-lifecycle.ts
7602
+ import { z as z104 } from "zod";
7603
+
7604
+ // packages/contracts/src/generated/client-release.ts
7605
+ var nativeClientRelease = {
7606
+ schemaVersion: 1,
7607
+ product: "native-marketing",
7608
+ version: "0.3.7",
7609
+ packages: {
7610
+ sdk: "@native-marketing/sdk",
7611
+ cli: "@native-marketing/cli"
7612
+ },
7613
+ contractVersion: 1,
7614
+ minimumClientVersion: "0.3.6",
7615
+ apiMajor: 1,
7616
+ nodeMajor: 24,
7617
+ changes: [
7618
+ "Unified SDK, CLI and MCP release discovery and compatibility checks.",
7619
+ "Configurable CLI update notifications, pins and compatible automatic updates.",
7620
+ "Upgrade verification and explicit continuation without replaying business commands."
7621
+ ]
7622
+ };
7623
+
7624
+ // packages/contracts/src/client-lifecycle.ts
7625
+ var nativeReleaseCapability = "com.native-marketing/client-release";
7626
+ var releaseVersionSchema = z104.string().max(128).regex(
7627
+ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/
7628
+ );
7629
+ var clientIdentitySchema = z104.object({
7630
+ version: releaseVersionSchema,
7631
+ contractVersion: z104.number().int().positive(),
7632
+ apiMajor: z104.number().int().positive()
7633
+ });
7634
+ var clientReleaseSchema = z104.object({
7635
+ schemaVersion: z104.literal(1),
7636
+ product: z104.literal("native-marketing"),
7637
+ version: releaseVersionSchema,
7638
+ packages: z104.object({
7639
+ sdk: z104.literal("@native-marketing/sdk"),
7640
+ cli: z104.literal("@native-marketing/cli")
7641
+ }),
7642
+ contractVersion: z104.number().int().positive(),
7643
+ minimumClientVersion: releaseVersionSchema,
7644
+ apiMajor: z104.number().int().positive(),
7645
+ nodeMajor: z104.number().int().positive(),
7646
+ changes: z104.array(z104.string().min(1).max(240)).max(8).default([])
7647
+ });
7648
+ var updatePolicySchema = z104.object({
7649
+ mode: z104.enum(["off", "notify", "auto"]).default("notify"),
7650
+ channel: z104.enum(["latest", "next", "beta"]).default("latest"),
7651
+ pin: releaseVersionSchema.nullable().default(null),
7652
+ intervalHours: z104.number().int().min(1).max(168).default(24)
7653
+ }).strict();
7654
+ var defaultUpdatePolicy = updatePolicySchema.parse({});
7655
+ function compareReleaseVersions(left, right) {
7656
+ const parse = (raw) => releaseVersionSchema.parse(raw).split("+")[0].split(/-(.*)/s);
7657
+ const a = parse(left), b = parse(right);
7658
+ const numbers = (x, y) => BigInt(x) < BigInt(y) ? -1 : BigInt(x) > BigInt(y) ? 1 : 0;
7659
+ const ac = a[0].split("."), bc = b[0].split(".");
7660
+ for (let i = 0; i < 3; i++) {
7661
+ const order = numbers(ac[i], bc[i]);
7662
+ if (order) return order;
7663
+ }
7664
+ if (a[1] === b[1]) return 0;
7665
+ if (!a[1] || !b[1]) return a[1] ? -1 : 1;
7666
+ const ap = a[1].split("."), bp = b[1].split(".");
7667
+ for (let i = 0; i < Math.max(ap.length, bp.length); i++) {
7668
+ const x = ap[i], y = bp[i];
7669
+ if (x === void 0 || y === void 0) return x === void 0 ? -1 : 1;
7670
+ if (x === y) continue;
7671
+ const xn = /^\d+$/.test(x), yn = /^\d+$/.test(y);
7672
+ return xn && yn ? numbers(x, y) : xn !== yn ? xn ? -1 : 1 : x < y ? -1 : 1;
7673
+ }
7674
+ return 0;
7675
+ }
7676
+ function assessNativeCompatibility(client, server) {
7677
+ client = clientIdentitySchema.parse(client);
7678
+ const result2 = (status, reason) => ({ status, reason, client, server: server ?? null });
7679
+ if (!server) return result2("UNKNOWN", "SERVER_METADATA_UNAVAILABLE");
7680
+ server = clientReleaseSchema.parse(server);
7681
+ if (client.contractVersion !== server.contractVersion)
7682
+ return result2("INCOMPATIBLE", "CONTRACT_MISMATCH");
7683
+ if (client.apiMajor !== server.apiMajor) return result2("INCOMPATIBLE", "API_MISMATCH");
7684
+ if (compareReleaseVersions(client.version, server.minimumClientVersion) < 0)
7685
+ return result2("INCOMPATIBLE", "CLIENT_TOO_OLD");
7686
+ return compareReleaseVersions(client.version, server.version) < 0 ? result2("UPGRADE_RECOMMENDED", "NEWER_CLIENT_AVAILABLE") : result2("COMPATIBLE", "CURRENT");
7687
+ }
7688
+
7689
+ // packages/contracts/src/client-update-policy.ts
7690
+ import { z as z105 } from "zod";
7691
+ var registryReleaseSchema = z105.object({
7692
+ name: z105.enum(["@native-marketing/sdk", "@native-marketing/cli"]),
7693
+ version: releaseVersionSchema,
7694
+ deprecated: z105.string().optional(),
7695
+ nativeMarketing: z105.object({
7696
+ contractVersion: z105.number().int().positive(),
7697
+ apiMajor: z105.number().int().positive(),
7698
+ nodeMajor: z105.number().int().positive(),
7699
+ changes: z105.array(z105.string().min(1).max(240)).max(8).optional()
7700
+ }).optional(),
7701
+ dist: z105.object({
7702
+ integrity: z105.string().max(512).regex(/^sha512-[A-Za-z0-9+/]{86}==$/)
7703
+ })
7704
+ });
7705
+ var clientUpdateStateSchema = z105.object({
7706
+ schemaVersion: z105.literal(1),
7707
+ policy: updatePolicySchema,
7708
+ lastCheck: z105.object({
7709
+ at: z105.number().int().nonnegative(),
7710
+ selector: z105.string().max(160),
7711
+ target: registryReleaseSchema
7712
+ }).strict().optional(),
7713
+ lastAttempt: z105.object({ at: z105.number().int().nonnegative(), selector: z105.string().max(160) }).strict().optional()
7714
+ }).strict();
7715
+ function planNativeUpdate(input) {
7716
+ const target = registryReleaseSchema.parse(input.target);
7717
+ const current = releaseVersionSchema.parse(input.currentVersion ?? nativeClientRelease.version);
7718
+ const policy = updatePolicySchema.parse(input.policy ?? {});
7719
+ const spec = `${target.name}@${target.version}`;
7720
+ const cli = target.name === nativeClientRelease.packages.cli;
7721
+ const result2 = (status, reason, eligible = false) => ({
7722
+ package: target.name,
7723
+ currentVersion: current,
7724
+ targetVersion: target.version,
7725
+ status,
7726
+ reason,
7727
+ eligibleForAutoUpdate: eligible,
7728
+ policy,
7729
+ commands: {
7730
+ npm: [
7731
+ "npm",
7732
+ "install",
7733
+ ...cli ? ["--global"] : ["--save-exact"],
7734
+ spec,
7735
+ "--ignore-scripts",
7736
+ "--registry=https://registry.npmjs.org/",
7737
+ "--@native-marketing:registry=https://registry.npmjs.org/"
7738
+ ],
7739
+ pnpm: ["pnpm", "add", ...cli ? ["--global"] : ["--save-exact"], spec, "--ignore-scripts"],
7740
+ yarn: cli ? null : ["yarn", "add", "--exact", spec]
7741
+ }
7742
+ });
7743
+ if (policy.pin && policy.pin !== target.version) return result2("BLOCKED", "PIN_MISMATCH");
7744
+ if (!policy.pin && policy.channel === "latest" && target.version.split("+")[0].includes("-"))
7745
+ return result2("BLOCKED", "CHANNEL_MISMATCH");
7746
+ const order = compareReleaseVersions(target.version, current);
7747
+ if (order < 0) return result2("BLOCKED", "DOWNGRADE_FORBIDDEN");
7748
+ if (target.deprecated !== void 0) return result2("BLOCKED", "DEPRECATED");
7749
+ if (!target.nativeMarketing) return result2("BLOCKED", "METADATA_UNAVAILABLE");
7750
+ const metadata = target.nativeMarketing;
7751
+ if (metadata.nodeMajor !== (input.nodeMajor ?? nativeClientRelease.nodeMajor))
7752
+ return result2("BLOCKED", "NODE_MISMATCH");
7753
+ if (metadata.contractVersion !== nativeClientRelease.contractVersion || metadata.apiMajor !== nativeClientRelease.apiMajor)
7754
+ return result2("BLOCKED", "CONTRACT_MISMATCH");
7755
+ if (input.server && assessNativeCompatibility(
7756
+ {
7757
+ version: target.version,
7758
+ contractVersion: metadata.contractVersion,
7759
+ apiMajor: metadata.apiMajor
7760
+ },
7761
+ input.server
7762
+ ).status === "INCOMPATIBLE")
7763
+ return result2("BLOCKED", "SERVER_INCOMPATIBLE");
7764
+ if (order === 0) return result2("CURRENT", "CURRENT");
7765
+ const from = current.split(/[.+-]/), to = target.version.split(/[.+-]/);
7766
+ const compatibleLine = from[0] === to[0] && (from[0] !== "0" || from[1] === to[1]);
7767
+ const stable = !current.split("+")[0].includes("-") && !target.version.split("+")[0].includes("-");
7768
+ return compatibleLine && stable ? result2("UPDATE_AVAILABLE", "COMPATIBLE_UPDATE", true) : result2("UPDATE_AVAILABLE", "REVIEW_REQUIRED");
7769
+ }
7770
+
7601
7771
  // packages/cli-client/src/errors.ts
7602
7772
  var CliError = class extends Error {
7603
7773
  constructor(code2, message, outcome = "NOT_SENT", details) {
@@ -7650,6 +7820,9 @@ function verifyCapabilityDefinition(raw, name, expectedHash) {
7650
7820
  }
7651
7821
  var CapabilityDefinitionCache = class {
7652
7822
  values = /* @__PURE__ */ new Map();
7823
+ clear() {
7824
+ this.values.clear();
7825
+ }
7653
7826
  remember(value) {
7654
7827
  const verified = verifyCapabilityDefinition(value, value.definition.name);
7655
7828
  const key2 = `${verified.definition.name}:AGENT_V3:${verified.schemaSha256}`;
@@ -7988,13 +8161,105 @@ function object2(value) {
7988
8161
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
7989
8162
  }
7990
8163
 
8164
+ // packages/cli-client/src/updates/version.ts
8165
+ import { readFile, realpath } from "node:fs/promises";
8166
+ import { dirname, join as join2 } from "node:path";
8167
+ function getNativeClientVersion() {
8168
+ return clientReleaseSchema.parse(nativeClientRelease);
8169
+ }
8170
+ async function getNativeCliVersion(executablePath) {
8171
+ const identity = { ...getNativeClientVersion(), sdkVersion: nativeClientRelease.version };
8172
+ if (!executablePath) return identity;
8173
+ try {
8174
+ const root = dirname(dirname(await realpath(executablePath)));
8175
+ const manifest = JSON.parse(await readFile(join2(root, "package.json"), "utf8"));
8176
+ if (manifest.name === nativeClientRelease.packages.cli)
8177
+ return { ...identity, version: releaseVersionSchema.parse(manifest.version) };
8178
+ } catch {
8179
+ }
8180
+ return identity;
8181
+ }
8182
+ function readNativeServerVersion(client) {
8183
+ const raw = client.getServerCapabilities()?.experimental?.[nativeReleaseCapability];
8184
+ let server;
8185
+ if (raw !== void 0) {
8186
+ const parsed = clientReleaseSchema.safeParse(raw);
8187
+ if (!parsed.success)
8188
+ throw new CliError(
8189
+ "INVALID_SERVER_RELEASE",
8190
+ "The advertised Native Marketing release contract is invalid."
8191
+ );
8192
+ server = parsed.data;
8193
+ }
8194
+ return {
8195
+ client: getNativeClientVersion(),
8196
+ serverInfo: client.getServerVersion() ?? null,
8197
+ mcpProtocolVersion: client.getNegotiatedProtocolVersion() ?? null,
8198
+ compatibility: assessNativeCompatibility(nativeClientRelease, server)
8199
+ };
8200
+ }
8201
+ function assertNativeCompatibility(result2, required = false) {
8202
+ if (result2.compatibility.status === "INCOMPATIBLE" || required && result2.compatibility.status === "UNKNOWN")
8203
+ throw new CliError(
8204
+ "CLIENT_INCOMPATIBLE",
8205
+ "Check version --server and update the client or connect to a compatible service. No business operation was sent.",
8206
+ "NOT_SENT",
8207
+ { compatibility: result2.compatibility }
8208
+ );
8209
+ }
8210
+ async function refreshNativeServerVersion(client, timeout) {
8211
+ const reply = await client.callTool(
8212
+ { name: "cf_get_client_release", arguments: {} },
8213
+ {
8214
+ timeout,
8215
+ allowInputRequired: true,
8216
+ toolDefinition: {
8217
+ name: "cf_get_client_release",
8218
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
8219
+ annotations: { readOnlyHint: true }
8220
+ }
8221
+ }
8222
+ ).catch(() => {
8223
+ throw new CliError(
8224
+ "VERSION_CHECK_UNAVAILABLE",
8225
+ "Cannot refresh the service release. Reconnect or refresh again before continuing."
8226
+ );
8227
+ });
8228
+ const text3 = reply.content?.find((item) => item.type === "text");
8229
+ let server;
8230
+ try {
8231
+ if (reply.isError) throw new Error();
8232
+ server = clientReleaseSchema.parse(
8233
+ reply.structuredContent ?? (text3?.type === "text" ? JSON.parse(text3.text) : null)
8234
+ );
8235
+ } catch {
8236
+ throw new CliError("INVALID_SERVER_RELEASE", "Cannot verify the current server release.");
8237
+ }
8238
+ const result2 = {
8239
+ ...readNativeServerVersion(client),
8240
+ compatibility: assessNativeCompatibility(nativeClientRelease, server)
8241
+ };
8242
+ return result2;
8243
+ }
8244
+
7991
8245
  // packages/cli-client/src/transport.ts
7992
- async function withClient(profile, timeout, operation) {
8246
+ async function withClient(profile, timeout, operation, options = {}) {
7993
8247
  const resource = resourceUrl(profile.resource);
7994
8248
  const accessToken = tokenText(profile.accessToken);
7995
8249
  const client = new Client(
7996
- { name: "content-factory-agent-cli", version: "0.1.0" },
7997
- { inputRequired: { autoFulfill: false } }
8250
+ { name: "@native-marketing/sdk", version: nativeClientRelease.version },
8251
+ {
8252
+ inputRequired: { autoFulfill: false },
8253
+ capabilities: {
8254
+ experimental: {
8255
+ [nativeReleaseCapability]: {
8256
+ version: nativeClientRelease.version,
8257
+ contractVersion: nativeClientRelease.contractVersion,
8258
+ apiMajor: nativeClientRelease.apiMajor
8259
+ }
8260
+ }
8261
+ }
8262
+ }
7998
8263
  );
7999
8264
  const transport = new StreamableHTTPClientTransport(resource, {
8000
8265
  authProvider: { token: async () => accessToken },
@@ -8015,6 +8280,10 @@ async function withClient(profile, timeout, operation) {
8015
8280
  "Cannot authenticate/connect to the configured CF resource. No business operation was sent."
8016
8281
  );
8017
8282
  }
8283
+ const version = readNativeServerVersion(client);
8284
+ if (!options.allowIncompatible)
8285
+ assertNativeCompatibility(version, options.requireCompatibility);
8286
+ options.onVersion?.(version);
8018
8287
  return await operation(client);
8019
8288
  } finally {
8020
8289
  await client.close().catch(() => void 0);
@@ -8882,43 +9151,97 @@ async function withNativeAgentClient(connection, action, options = {}) {
8882
9151
  return withClient(
8883
9152
  connection,
8884
9153
  timeout,
8885
- (client) => action({
8886
- readPackageFile: (input) => readNativePackageFile(connection, input, timeout),
8887
- readProjectMediaFile: (input) => readNativeProjectMediaFile(connection, input, timeout),
8888
- uploadProjectMedia: (file) => uploadNativeProjectMedia(connection, file, timeout),
8889
- uploadMaterial: (file) => uploadNativeMaterial(connection, file, timeout),
8890
- inspectTool: (name) => inspectNativeTool(client, name, timeout, definitions),
8891
- callTool: (name, input = {}, options2) => callNativeTool(client, name, input, connection.resource, options2, timeout, definitions),
8892
- listToolsPage: (input = {}) => listNativeToolsPage(client, input, timeout),
8893
- readCapabilitySchemaPage: (input) => readNativeCapabilitySchemaPage(client, input, timeout),
8894
- readDocumentPage: (input) => readNativeDocumentPage(client, input, timeout),
8895
- getBusinessSummary: (input) => getNativeBusinessSummary(client, input, timeout),
8896
- callCapability: (input) => callNativeCapability(client, input, timeout),
8897
- readCapability: (input) => readNativeCapability(client, input, timeout),
8898
- writeCapability: (input) => writeNativeCapability(client, input, timeout),
8899
- describeCapabilitySummary: (name) => readNativeCapabilityDescriptor(client, name, timeout),
8900
- searchBusiness: (input) => searchNativeBusiness(client, input, timeout),
8901
- readBusinessPage: (input) => readNativeBusinessPage(client, input, timeout),
8902
- findCapabilities: (input) => findNativeCapabilities(client, input, timeout),
8903
- discoverCapabilities: async (input) => {
8904
- const page2 = await findAgentCapabilities(client, input, timeout);
8905
- for (const item of page2.items) if (item.definition) definitions.remember(item.definition);
8906
- return page2;
8907
- },
8908
- describeCapability: async (name, expectedHash) => {
8909
- const cached = expectedHash ? definitions.get(name, expectedHash) : void 0;
8910
- if (cached) return cached;
8911
- const definition = await describeNativeCapability(client, name, timeout);
8912
- if (expectedHash && definition.schemaSha256 !== expectedHash)
8913
- throw new CliError(
8914
- "CAPABILITY_SCHEMA_CHANGED",
8915
- "Discover the current definition again.",
8916
- "REJECTED"
8917
- );
8918
- definitions.remember(definition);
8919
- return definition;
8920
- }
8921
- })
9154
+ (client) => {
9155
+ let version = readNativeServerVersion(client);
9156
+ let versionUnverified = false;
9157
+ client.setNotificationHandler("notifications/tools/list_changed", () => {
9158
+ definitions.clear();
9159
+ });
9160
+ return action(
9161
+ new Proxy(
9162
+ {
9163
+ getVersion: () => structuredClone(version),
9164
+ refreshVersion: async () => {
9165
+ definitions.clear();
9166
+ try {
9167
+ version = await refreshNativeServerVersion(client, timeout);
9168
+ versionUnverified = false;
9169
+ } catch (error) {
9170
+ version = { ...version, compatibility: assessNativeCompatibility(version.client) };
9171
+ versionUnverified = true;
9172
+ throw error;
9173
+ }
9174
+ assertNativeCompatibility(version, options.requireCompatibility);
9175
+ options.onVersion?.(version);
9176
+ return structuredClone(version);
9177
+ },
9178
+ readPackageFile: (input) => readNativePackageFile(connection, input, timeout),
9179
+ readProjectMediaFile: (input) => readNativeProjectMediaFile(connection, input, timeout),
9180
+ uploadProjectMedia: (file) => uploadNativeProjectMedia(connection, file, timeout),
9181
+ uploadMaterial: (file) => uploadNativeMaterial(connection, file, timeout),
9182
+ inspectTool: (name) => inspectNativeTool(client, name, timeout, definitions),
9183
+ callTool: (name, input = {}, options2) => callNativeTool(
9184
+ client,
9185
+ name,
9186
+ input,
9187
+ connection.resource,
9188
+ options2,
9189
+ timeout,
9190
+ definitions
9191
+ ),
9192
+ listToolsPage: (input = {}) => listNativeToolsPage(client, input, timeout),
9193
+ readCapabilitySchemaPage: (input) => readNativeCapabilitySchemaPage(client, input, timeout),
9194
+ readDocumentPage: (input) => readNativeDocumentPage(client, input, timeout),
9195
+ getBusinessSummary: (input) => getNativeBusinessSummary(client, input, timeout),
9196
+ callCapability: (input) => callNativeCapability(client, input, timeout),
9197
+ readCapability: (input) => readNativeCapability(client, input, timeout),
9198
+ writeCapability: (input) => writeNativeCapability(client, input, timeout),
9199
+ describeCapabilitySummary: (name) => readNativeCapabilityDescriptor(client, name, timeout),
9200
+ searchBusiness: (input) => searchNativeBusiness(client, input, timeout),
9201
+ readBusinessPage: (input) => readNativeBusinessPage(client, input, timeout),
9202
+ findCapabilities: (input) => findNativeCapabilities(client, input, timeout),
9203
+ discoverCapabilities: async (input) => {
9204
+ const page2 = await findAgentCapabilities(client, input, timeout);
9205
+ for (const item of page2.items)
9206
+ if (item.definition) definitions.remember(item.definition);
9207
+ return page2;
9208
+ },
9209
+ describeCapability: async (name, expectedHash) => {
9210
+ const cached = expectedHash ? definitions.get(name, expectedHash) : void 0;
9211
+ if (cached) return cached;
9212
+ const definition = await describeNativeCapability(client, name, timeout);
9213
+ if (expectedHash && definition.schemaSha256 !== expectedHash)
9214
+ throw new CliError(
9215
+ "CAPABILITY_SCHEMA_CHANGED",
9216
+ "Discover the current definition again.",
9217
+ "REJECTED"
9218
+ );
9219
+ definitions.remember(definition);
9220
+ return definition;
9221
+ }
9222
+ },
9223
+ {
9224
+ get(target, name, receiver) {
9225
+ const method = Reflect.get(target, name, receiver);
9226
+ if (typeof method !== "function" || name === "getVersion" || name === "refreshVersion")
9227
+ return method;
9228
+ return async (...args) => {
9229
+ if (versionUnverified)
9230
+ throw new CliError(
9231
+ "CLIENT_VERSION_UNVERIFIED",
9232
+ "Verify the service release again or reconnect before continuing. No business operation was sent.",
9233
+ "NOT_SENT",
9234
+ { compatibility: version.compatibility }
9235
+ );
9236
+ assertNativeCompatibility(version, options.requireCompatibility);
9237
+ return Reflect.apply(method, target, args);
9238
+ };
9239
+ }
9240
+ }
9241
+ )
9242
+ );
9243
+ },
9244
+ options
8922
9245
  );
8923
9246
  }
8924
9247
  function checkTimeout4(timeout) {
@@ -9073,9 +9396,22 @@ var values = /* @__PURE__ */ new Set([
9073
9396
  "scope",
9074
9397
  "job",
9075
9398
  "view",
9076
- "schema-hash"
9399
+ "schema-hash",
9400
+ "mode",
9401
+ "channel",
9402
+ "pin",
9403
+ "interval-hours"
9404
+ ]);
9405
+ var flags = /* @__PURE__ */ new Set([
9406
+ "json",
9407
+ "stdin",
9408
+ "help",
9409
+ "version",
9410
+ "server",
9411
+ "strict",
9412
+ "allow-breaking",
9413
+ "dry-run"
9077
9414
  ]);
9078
- var flags = /* @__PURE__ */ new Set(["json", "stdin", "help"]);
9079
9415
  function parseArgs(args) {
9080
9416
  const options = {};
9081
9417
  const words = [];
@@ -9154,8 +9490,8 @@ async function login(store, rawResource, scope2, timeout, notify) {
9154
9490
  const state = randomBytes(32).toString("base64url");
9155
9491
  let resolveCode;
9156
9492
  let rejectCode;
9157
- const codePromise = new Promise((resolve, reject) => {
9158
- resolveCode = resolve;
9493
+ const codePromise = new Promise((resolve2, reject) => {
9494
+ resolveCode = resolve2;
9159
9495
  rejectCode = reject;
9160
9496
  });
9161
9497
  void codePromise.catch(() => void 0);
@@ -9186,9 +9522,9 @@ async function login(store, rawResource, scope2, timeout, notify) {
9186
9522
  });
9187
9523
  let timer;
9188
9524
  try {
9189
- await new Promise((resolve, reject) => {
9525
+ await new Promise((resolve2, reject) => {
9190
9526
  server.once("error", reject);
9191
- server.listen(0, "127.0.0.1", resolve);
9527
+ server.listen(0, "127.0.0.1", resolve2);
9192
9528
  });
9193
9529
  const address = server.address();
9194
9530
  if (!address || typeof address === "string")
@@ -9258,7 +9594,7 @@ Waiting up to 5 minutes. For remote/headless Agents, use auth import with a prov
9258
9594
  } finally {
9259
9595
  if (timer) clearTimeout(timer);
9260
9596
  server.closeAllConnections();
9261
- await new Promise((resolve) => server.close(() => resolve()));
9597
+ await new Promise((resolve2) => server.close(() => resolve2()));
9262
9598
  }
9263
9599
  });
9264
9600
  }
@@ -9333,6 +9669,10 @@ var fullHelp = {
9333
9669
  executable: "cf-agent",
9334
9670
  transport: "OAuth-authenticated HTTPS MCP; no local database",
9335
9671
  commands: [
9672
+ "version [--server [--strict] [--profile <name>]]",
9673
+ "update check [--channel latest|next|beta] [--pin <exact-version>|none]",
9674
+ "update apply [--dry-run] [--allow-breaking] [--channel latest|next|beta] [--pin <exact-version>|none]",
9675
+ "update policy [--mode off|notify|auto] [--channel latest|next|beta] [--pin <exact-version>|none] [--interval-hours <1..168>]",
9336
9676
  "auth login --resource <url> [--scope <scopes>] [--profile <name>]",
9337
9677
  "auth import --resource <url> --token-file <private-file> [--profile <name>]",
9338
9678
  "auth status|refresh|logout [--profile <name>]",
@@ -9376,6 +9716,8 @@ var help = {
9376
9716
  executable: "cf-agent",
9377
9717
  transport: "OAuth-authenticated MCP; no local database",
9378
9718
  commands: [
9719
+ "version [--server [--strict]]",
9720
+ "update check|apply|policy (help update for channels, pins and automatic updates)",
9379
9721
  "auth login --resource <url> [--scope <scopes>]",
9380
9722
  "search [<text>] [--input-file <query.json>|--stdin]",
9381
9723
  "business read (--input-file <ref-and-page.json>|--stdin)",
@@ -9520,6 +9862,21 @@ function resolveBaseCommand(parsed) {
9520
9862
  throw new CliError("INVALID_COMMAND", "Run help for supported commands.");
9521
9863
  }
9522
9864
  function resolveCommand(parsed) {
9865
+ if ([
9866
+ "mode",
9867
+ "channel",
9868
+ "pin",
9869
+ "interval-hours",
9870
+ "version",
9871
+ "server",
9872
+ "strict",
9873
+ "allow-breaking",
9874
+ "dry-run"
9875
+ ].some((key2) => parsed.options[key2]))
9876
+ throw new CliError(
9877
+ "INVALID_OPTION",
9878
+ "Version/update options apply only to version/update commands."
9879
+ );
9523
9880
  const removed = {
9524
9881
  doctor: "context",
9525
9882
  "knowledge sync-all": "knowledge discover",
@@ -9654,14 +10011,430 @@ function validateCommandInput(parsed, input) {
9654
10011
  throw new CliError("INVALID_PAGINATION", "Supply only an optional nonempty cursor.");
9655
10012
  }
9656
10013
 
10014
+ // packages/cli-client/src/updates/registry.ts
10015
+ async function checkNativeUpdates(options = {}) {
10016
+ const policy = updatePolicySchema.parse(options.policy ?? {});
10017
+ const current = releaseVersionSchema.safeParse(
10018
+ options.currentVersion ?? nativeClientRelease.version
10019
+ );
10020
+ if (!current.success)
10021
+ throw new CliError(
10022
+ "INVALID_UPDATE_VERSION",
10023
+ "The installed version must be an exact semantic version."
10024
+ );
10025
+ const timeout = options.timeoutMs ?? 5e3;
10026
+ if (!Number.isInteger(timeout) || timeout < 100 || timeout > 12e4)
10027
+ throw new CliError("INVALID_TIMEOUT", "Timeout must be 100\u2013120000 milliseconds.");
10028
+ const name = nativeClientRelease.packages[options.package ?? "sdk"];
10029
+ if (!name) throw new CliError("INVALID_UPDATE_PACKAGE", "Choose sdk or cli.");
10030
+ const selector = policy.pin ?? policy.channel;
10031
+ const url = `https://registry.npmjs.org/${encodeURIComponent(name)}/${encodeURIComponent(selector)}`;
10032
+ let target;
10033
+ try {
10034
+ const response = await (options.fetch ?? fetch)(url, {
10035
+ redirect: "error",
10036
+ credentials: "omit",
10037
+ signal: AbortSignal.timeout(timeout),
10038
+ headers: { accept: "application/json" }
10039
+ });
10040
+ if (!response.ok || !response.body) throw new Error();
10041
+ const reader = response.body.getReader();
10042
+ const chunks = [];
10043
+ let size = 0;
10044
+ try {
10045
+ for (; ; ) {
10046
+ const { done, value } = await reader.read();
10047
+ if (done) break;
10048
+ size += value.byteLength;
10049
+ if (size > 128 * 1024) throw new Error();
10050
+ chunks.push(value);
10051
+ }
10052
+ } finally {
10053
+ await reader.cancel().catch(() => void 0);
10054
+ reader.releaseLock();
10055
+ }
10056
+ target = registryReleaseSchema.parse(JSON.parse(Buffer.concat(chunks).toString("utf8")));
10057
+ if (target.name !== name || policy.pin && target.version !== policy.pin) throw new Error();
10058
+ } catch {
10059
+ throw new CliError(
10060
+ "UPDATE_CHECK_UNAVAILABLE",
10061
+ "Cannot verify release metadata from the public npm registry. Existing software and credentials are unchanged."
10062
+ );
10063
+ }
10064
+ return {
10065
+ checkedAt: Date.now(),
10066
+ target,
10067
+ plan: planNativeUpdate({
10068
+ target,
10069
+ policy,
10070
+ nodeMajor: Number(process.versions.node.split(".")[0]),
10071
+ currentVersion: current.data,
10072
+ ...options.server ? { server: options.server } : {}
10073
+ })
10074
+ };
10075
+ }
10076
+
10077
+ // packages/cli-client/src/updates/store.ts
10078
+ import { lstat as lstat2, open as open6, rename as rename2, unlink as unlink2 } from "node:fs/promises";
10079
+ import { join as join3 } from "node:path";
10080
+ import { randomUUID as randomUUID3 } from "node:crypto";
10081
+ var UpdateStore = class {
10082
+ files;
10083
+ constructor(env) {
10084
+ const base2 = new ProfileStore(env).directory;
10085
+ this.files = new ProfileStore({ CF_AGENT_CONFIG_DIR: join3(base2, "client-lifecycle") }, "state");
10086
+ }
10087
+ async read() {
10088
+ try {
10089
+ await lstat2(this.files.path);
10090
+ } catch (error) {
10091
+ if (error.code === "ENOENT")
10092
+ return { schemaVersion: 1, policy: updatePolicySchema.parse({}) };
10093
+ throw error;
10094
+ }
10095
+ let raw;
10096
+ try {
10097
+ raw = JSON.parse(await readFileBounded(this.files.path, true));
10098
+ } catch {
10099
+ throw new CliError(
10100
+ "INVALID_UPDATE_CONFIGURATION",
10101
+ "Update state is unreadable. Inspect the client-lifecycle configuration."
10102
+ );
10103
+ }
10104
+ const parsed = clientUpdateStateSchema.safeParse(raw);
10105
+ if (!parsed.success)
10106
+ throw new CliError(
10107
+ "INVALID_UPDATE_CONFIGURATION",
10108
+ "Update state is invalid. Inspect the client-lifecycle configuration."
10109
+ );
10110
+ return parsed.data;
10111
+ }
10112
+ locked(action) {
10113
+ return this.files.locked(action);
10114
+ }
10115
+ async change(action) {
10116
+ return this.locked(async () => {
10117
+ const state = clientUpdateStateSchema.parse(action(await this.read()));
10118
+ const temporary = `${this.files.path}.${randomUUID3()}.tmp`;
10119
+ const file = await open6(temporary, "wx", 384);
10120
+ try {
10121
+ try {
10122
+ await file.writeFile(JSON.stringify(state) + "\n");
10123
+ await file.sync();
10124
+ } finally {
10125
+ await file.close();
10126
+ }
10127
+ await rename2(temporary, this.files.path);
10128
+ } finally {
10129
+ await unlink2(temporary).catch(() => void 0);
10130
+ }
10131
+ return state;
10132
+ });
10133
+ }
10134
+ };
10135
+ function effectiveUpdatePolicy(policy, env) {
10136
+ const parsed = updatePolicySchema.safeParse({
10137
+ ...policy,
10138
+ ...env.CF_AGENT_UPDATE_MODE ? { mode: env.CF_AGENT_UPDATE_MODE } : {},
10139
+ ...env.CF_AGENT_UPDATE_CHANNEL ? { channel: env.CF_AGENT_UPDATE_CHANNEL } : {},
10140
+ ...env.CF_AGENT_UPDATE_PIN ? { pin: env.CF_AGENT_UPDATE_PIN === "none" ? null : env.CF_AGENT_UPDATE_PIN } : {}
10141
+ });
10142
+ if (!parsed.success)
10143
+ throw new CliError("INVALID_UPDATE_CONFIGURATION", "Invalid update environment policy.");
10144
+ return parsed.data;
10145
+ }
10146
+
10147
+ // packages/cli-client/src/updates/install.ts
10148
+ import { execFile } from "node:child_process";
10149
+ import { promisify } from "node:util";
10150
+ import { readFile as readFile2, realpath as realpath2 } from "node:fs/promises";
10151
+ import { dirname as dirname2, join as join4, resolve } from "node:path";
10152
+ var execute = promisify(execFile);
10153
+ async function installCliUpdate(input) {
10154
+ const plan = planNativeUpdate({
10155
+ target: input.target,
10156
+ ...input.currentVersion ? { currentVersion: input.currentVersion } : {},
10157
+ policy: input.policy,
10158
+ nodeMajor: Number(process.versions.node.split(".")[0])
10159
+ });
10160
+ if (plan.package !== nativeClientRelease.packages.cli || plan.status !== "UPDATE_AVAILABLE" || !plan.eligibleForAutoUpdate && !input.allowBreaking)
10161
+ throw new CliError(
10162
+ "UPDATE_POLICY_BLOCKED",
10163
+ "This release cannot be installed under the selected policy.",
10164
+ "NOT_SENT",
10165
+ { plan }
10166
+ );
10167
+ if (!input.executablePath || process.platform === "win32")
10168
+ throw new CliError(
10169
+ "MANAGED_UPDATE_UNAVAILABLE",
10170
+ "Use the package manager that owns this installation.",
10171
+ "NOT_SENT",
10172
+ { plan }
10173
+ );
10174
+ const environment = Object.fromEntries(
10175
+ Object.entries(input.env).filter(
10176
+ ([key2, value]) => value !== void 0 && !/^(?:CF_AGENT_|CONTENT_FACTORY_)/i.test(key2)
10177
+ )
10178
+ );
10179
+ environment.CF_AGENT_UPDATE_MODE = "off";
10180
+ environment.NODE_OPTIONS = "--max-old-space-size=384";
10181
+ const options = { env: environment, timeout: input.timeoutMs, maxBuffer: 128 * 1024 };
10182
+ let packageRoot, prefix;
10183
+ try {
10184
+ packageRoot = dirname2(dirname2(await realpath2(input.executablePath)));
10185
+ const manifest = JSON.parse(await readFile2(join4(packageRoot, "package.json"), "utf8"));
10186
+ const root = await realpath2(
10187
+ (await execute("npm", ["root", "--global"], options)).stdout.trim()
10188
+ );
10189
+ prefix = await realpath2((await execute("npm", ["prefix", "--global"], options)).stdout.trim());
10190
+ if (manifest.name !== nativeClientRelease.packages.cli || manifest.version !== plan.currentVersion || packageRoot !== await realpath2(join4(root, "@native-marketing/cli")) || packageRoot !== resolve(prefix, "lib/node_modules/@native-marketing/cli"))
10191
+ throw new Error();
10192
+ } catch {
10193
+ throw new CliError(
10194
+ "MANAGED_UPDATE_UNAVAILABLE",
10195
+ "Automatic installation supports the active npm global installation. Use the owning project/package manager for other installs.",
10196
+ "NOT_SENT",
10197
+ { plan }
10198
+ );
10199
+ }
10200
+ const args = [
10201
+ "install",
10202
+ "--global",
10203
+ "--prefix",
10204
+ prefix,
10205
+ `${plan.package}@${plan.targetVersion}`,
10206
+ "--registry=https://registry.npmjs.org/",
10207
+ "--@native-marketing:registry=https://registry.npmjs.org/",
10208
+ "--ignore-scripts",
10209
+ "--no-audit",
10210
+ "--no-fund"
10211
+ ];
10212
+ const recovery = [
10213
+ "npm",
10214
+ "install",
10215
+ "--global",
10216
+ "--prefix",
10217
+ prefix,
10218
+ `${plan.package}@${plan.currentVersion}`,
10219
+ "--ignore-scripts",
10220
+ "--registry=https://registry.npmjs.org/",
10221
+ "--@native-marketing:registry=https://registry.npmjs.org/"
10222
+ ];
10223
+ const store = new UpdateStore(input.env);
10224
+ return store.locked(async () => {
10225
+ if (input.automatic) {
10226
+ const latest = effectiveUpdatePolicy((await store.read()).policy, input.env);
10227
+ if (latest.mode !== "auto" || (latest.pin ?? latest.channel) !== (input.policy.pin ?? input.policy.channel) || !planNativeUpdate({
10228
+ target: input.target,
10229
+ currentVersion: plan.currentVersion,
10230
+ policy: latest,
10231
+ nodeMajor: Number(process.versions.node.split(".")[0])
10232
+ }).eligibleForAutoUpdate)
10233
+ throw new CliError(
10234
+ "UPDATE_POLICY_CHANGED",
10235
+ "Automatic update policy changed. No installation or business operation was sent."
10236
+ );
10237
+ }
10238
+ const before = JSON.parse(await readFile2(join4(packageRoot, "package.json"), "utf8"));
10239
+ if (before.name !== plan.package || before.version !== plan.currentVersion)
10240
+ throw new CliError(
10241
+ "UPDATE_INSTALLATION_CHANGED",
10242
+ "The installed version changed. Check version before updating again."
10243
+ );
10244
+ try {
10245
+ await execute("npm", args, { ...options, cwd: prefix });
10246
+ const installed = JSON.parse(await readFile2(join4(packageRoot, "package.json"), "utf8"));
10247
+ if (installed.name !== plan.package || releaseVersionSchema.parse(installed.version) !== plan.targetVersion)
10248
+ throw new Error();
10249
+ const verified = await execute(
10250
+ process.execPath,
10251
+ [join4(packageRoot, "dist/cf.js"), "version", "--json"],
10252
+ options
10253
+ );
10254
+ const result2 = JSON.parse(verified.stdout);
10255
+ if (!result2.ok || result2.data?.version !== plan.targetVersion || result2.data?.sdkVersion !== plan.targetVersion)
10256
+ throw new Error();
10257
+ return {
10258
+ state: "UPDATED",
10259
+ package: plan.package,
10260
+ previousVersion: plan.currentVersion,
10261
+ installedVersion: plan.targetVersion,
10262
+ restartRequired: true,
10263
+ originalCommandExecuted: false,
10264
+ recovery
10265
+ };
10266
+ } catch {
10267
+ throw new CliError(
10268
+ "UPDATE_OUTCOME_UNKNOWN",
10269
+ "The installation may have changed. Inspect version before another update; use the recovery command if needed. The original business command was not sent.",
10270
+ "UNKNOWN",
10271
+ { expectedVersion: plan.targetVersion, recovery, originalCommandExecuted: false }
10272
+ );
10273
+ }
10274
+ });
10275
+ }
10276
+
10277
+ // packages/cli-client/src/updates/commands.ts
10278
+ function validateOptions(parsed, names) {
10279
+ if (Object.keys(parsed.options).some((name) => !["json", "timeout-ms", ...names].includes(name)))
10280
+ throw new CliError("INVALID_OPTION", "Unsupported version/update option. Use help update.");
10281
+ }
10282
+ function selectedPolicy(saved, parsed) {
10283
+ const options = parsed.options;
10284
+ const result2 = updatePolicySchema.safeParse({
10285
+ ...saved,
10286
+ ...options.mode ? { mode: options.mode } : {},
10287
+ ...options.channel ? { channel: options.channel } : {},
10288
+ ...options.pin ? { pin: options.pin === "none" ? null : options.pin } : {},
10289
+ ...options["interval-hours"] ? { intervalHours: Number(options["interval-hours"]) } : {}
10290
+ });
10291
+ if (!result2.success)
10292
+ throw new CliError(
10293
+ "INVALID_UPDATE_POLICY",
10294
+ "Use mode off/notify/auto, channel latest/next/beta, an exact pin or none, and interval-hours 1\u2013168."
10295
+ );
10296
+ return result2.data;
10297
+ }
10298
+ async function runLifecycleCommand(parsed, env, executablePath) {
10299
+ const { words, options, timeout } = parsed;
10300
+ if (options.version || words[0] === "version") {
10301
+ if (options.version && words.length !== 0 || !options.version && words.length !== 1)
10302
+ throw new CliError("INVALID_COMMAND", "Use version or --version.");
10303
+ validateOptions(parsed, ["version", "server", "strict", "profile"]);
10304
+ if (options.strict && !options.server)
10305
+ throw new CliError("INVALID_OPTION", "--strict requires version --server.");
10306
+ if (options.profile && !options.server)
10307
+ throw new CliError("INVALID_OPTION", "--profile requires version --server.");
10308
+ return options.server ? null : { command: "version", data: await getNativeCliVersion(executablePath) };
10309
+ }
10310
+ if (words[0] !== "update") return null;
10311
+ if (words.length !== 2 || !["check", "apply", "policy"].includes(words[1]))
10312
+ throw new CliError("INVALID_COMMAND", "Use update check/apply/policy.");
10313
+ const policyCommand = words[1] === "policy";
10314
+ validateOptions(
10315
+ parsed,
10316
+ policyCommand ? ["mode", "channel", "pin", "interval-hours"] : ["channel", "pin", ...words[1] === "apply" ? ["allow-breaking", "dry-run"] : []]
10317
+ );
10318
+ const store = new UpdateStore(env);
10319
+ const state = await store.read();
10320
+ if (policyCommand) {
10321
+ const changes = Object.keys(options).some(
10322
+ (name) => ["mode", "channel", "pin", "interval-hours"].includes(name)
10323
+ );
10324
+ const saved = changes ? await store.change((current) => ({
10325
+ ...current,
10326
+ policy: selectedPolicy(current.policy, parsed)
10327
+ })) : state;
10328
+ return {
10329
+ command: "update policy",
10330
+ data: {
10331
+ saved: saved.policy,
10332
+ effective: effectiveUpdatePolicy(saved.policy, env),
10333
+ lastCheckedAt: saved.lastCheck?.at ?? null
10334
+ }
10335
+ };
10336
+ }
10337
+ const policy = selectedPolicy(effectiveUpdatePolicy(state.policy, env), parsed);
10338
+ const currentVersion = (await getNativeCliVersion(executablePath)).version;
10339
+ const checked = await checkNativeUpdates({
10340
+ package: "cli",
10341
+ currentVersion,
10342
+ policy,
10343
+ timeoutMs: timeout
10344
+ });
10345
+ await store.change((current) => ({
10346
+ ...current,
10347
+ lastAttempt: { at: checked.checkedAt, selector: policy.pin ?? policy.channel },
10348
+ lastCheck: {
10349
+ at: checked.checkedAt,
10350
+ selector: policy.pin ?? policy.channel,
10351
+ target: checked.target
10352
+ }
10353
+ }));
10354
+ if (words[1] === "check" || options["dry-run"] || checked.plan.status === "CURRENT")
10355
+ return { command: words.join(" "), data: checked };
10356
+ const data = await installCliUpdate({
10357
+ target: checked.target,
10358
+ currentVersion,
10359
+ policy,
10360
+ timeoutMs: timeout,
10361
+ env,
10362
+ allowBreaking: Boolean(options["allow-breaking"]),
10363
+ ...executablePath ? { executablePath } : {}
10364
+ });
10365
+ return { command: "update apply", data };
10366
+ }
10367
+ async function maybeAutoUpdate(env, io, timeout) {
10368
+ if (!io.executablePath || env.CF_AGENT_UPDATE_MODE === "off" || env.CI && !env.CF_AGENT_UPDATE_MODE)
10369
+ return null;
10370
+ const notify = io.notify ?? ((message) => process.stderr.write(message + "\n"));
10371
+ try {
10372
+ const store = new UpdateStore(env);
10373
+ const state = await store.read();
10374
+ const policy = effectiveUpdatePolicy(state.policy, env);
10375
+ if (policy.mode === "off") return null;
10376
+ const now = Date.now(), selector = policy.pin ?? policy.channel;
10377
+ const age = state.lastAttempt ? now - state.lastAttempt.at : Infinity;
10378
+ if (state.lastAttempt?.selector === selector && age >= 0 && age < policy.intervalHours * 36e5)
10379
+ return null;
10380
+ let claimed = false;
10381
+ await store.change((current) => {
10382
+ const age2 = current.lastAttempt ? now - current.lastAttempt.at : Infinity;
10383
+ if (current.lastAttempt?.selector === selector && age2 >= 0 && age2 < policy.intervalHours * 36e5)
10384
+ return current;
10385
+ claimed = true;
10386
+ return { ...current, lastAttempt: { at: now, selector } };
10387
+ });
10388
+ if (!claimed) return null;
10389
+ const currentVersion = (await getNativeCliVersion(io.executablePath)).version;
10390
+ const checked = await checkNativeUpdates({
10391
+ package: "cli",
10392
+ currentVersion,
10393
+ policy,
10394
+ timeoutMs: Math.min(timeout, 2e3)
10395
+ });
10396
+ await store.change((current) => ({
10397
+ ...current,
10398
+ lastCheck: { at: checked.checkedAt, selector, target: checked.target }
10399
+ }));
10400
+ if (checked.plan.status === "CURRENT" || checked.plan.reason === "DOWNGRADE_FORBIDDEN" && !policy.pin)
10401
+ return null;
10402
+ if (policy.mode === "auto" && checked.plan.eligibleForAutoUpdate) {
10403
+ const data = await installCliUpdate({
10404
+ target: checked.target,
10405
+ currentVersion,
10406
+ automatic: true,
10407
+ policy,
10408
+ executablePath: io.executablePath,
10409
+ env,
10410
+ timeoutMs: timeout
10411
+ });
10412
+ notify(
10413
+ `Native Marketing updated to ${checked.plan.targetVersion}. Run the original command again.`
10414
+ );
10415
+ return { command: "update auto", data };
10416
+ }
10417
+ notify(
10418
+ `Native Marketing ${nativeClientRelease.version}: update ${checked.plan.targetVersion} (${checked.plan.reason}). Use update check or update apply.`
10419
+ );
10420
+ } catch (error) {
10421
+ if (error instanceof CliError && error.code === "UPDATE_OUTCOME_UNKNOWN") throw error;
10422
+ notify("Native Marketing update check/install is unavailable; use update check for details.");
10423
+ }
10424
+ return null;
10425
+ }
10426
+
9657
10427
  // packages/cli-client/src/cli/run.ts
9658
10428
  async function runNetworkCli(args, env = process.env, io = {}, executable = "cf") {
9659
10429
  let command5 = executable;
9660
10430
  try {
9661
10431
  const parsed = parseArgs(args);
9662
10432
  const { words, options, timeout } = parsed;
9663
- if (!words.length || words[0] === "help" || options.help)
10433
+ if ((!words.length || words[0] === "help" || options.help) && !options.version)
9664
10434
  return success("help", { ...commandHelp(words), executable });
10435
+ const local = await runLifecycleCommand(parsed, env, io.executablePath);
10436
+ if (local) return success(local.command, local.data);
10437
+ const remoteVersion = (words.join(" ") === "version" || options.version && !words.length) && options.server;
9665
10438
  const store = new ProfileStore(env, String(options.profile ?? "default"));
9666
10439
  if (words[0] === "auth") {
9667
10440
  if (words.length !== 2)
@@ -9733,10 +10506,14 @@ async function runNetworkCli(args, env = process.env, io = {}, executable = "cf"
9733
10506
  "AUTH_OPTION_MISUSE",
9734
10507
  "Resource/token/scope options belong to auth commands. Business commands use the bound profile or CF_AGENT_RESOURCE and CF_AGENT_ACCESS_TOKEN together."
9735
10508
  );
9736
- resolveCommand(parsed);
9737
- command5 = words[0] === "search" ? "search" : words[0] === "capabilities" ? words.slice(0, 2).join(" ") : words.join(" ");
10509
+ if (!remoteVersion) resolveCommand(parsed);
10510
+ command5 = remoteVersion ? "version" : words[0] === "search" ? "search" : words[0] === "capabilities" ? words.slice(0, 2).join(" ") : words.join(" ");
10511
+ if (!remoteVersion) {
10512
+ const automatic = await maybeAutoUpdate(env, io, timeout);
10513
+ if (automatic) return success(automatic.command, automatic.data);
10514
+ }
9738
10515
  const input = options.stdin ? objectJson(await readStream(io.stdin ?? process.stdin)) : typeof options["input-file"] === "string" ? objectJson(await readFileBounded(options["input-file"])) : {};
9739
- validateCommandInput(parsed, input);
10516
+ if (!remoteVersion) validateCommandInput(parsed, input);
9740
10517
  let profile;
9741
10518
  if (env.CF_AGENT_ACCESS_TOKEN || env.CF_AGENT_RESOURCE) {
9742
10519
  if (options.profile || !env.CF_AGENT_ACCESS_TOKEN || !env.CF_AGENT_RESOURCE)
@@ -9749,7 +10526,18 @@ async function runNetworkCli(args, env = process.env, io = {}, executable = "cf"
9749
10526
  accessToken: tokenText(env.CF_AGENT_ACCESS_TOKEN)
9750
10527
  };
9751
10528
  } else profile = await refreshProfile(store, timeout);
9752
- const data = resolveCommand(parsed).mode === "package-file" ? await downloadPackageFile(profile, input, String(options["output-file"]), timeout) : resolveCommand(parsed).mode === "media-file" ? await downloadProjectMedia(profile, input, String(options["output-file"]), timeout) : resolveCommand(parsed).mode === "upload" ? await uploadLocalMaterial(
10529
+ const data = remoteVersion ? await withClient(
10530
+ profile,
10531
+ timeout,
10532
+ async (client) => ({
10533
+ ...readNativeServerVersion(client),
10534
+ cli: await getNativeCliVersion(io.executablePath)
10535
+ }),
10536
+ {
10537
+ allowIncompatible: !options.strict,
10538
+ requireCompatibility: Boolean(options.strict)
10539
+ }
10540
+ ) : resolveCommand(parsed).mode === "package-file" ? await downloadPackageFile(profile, input, String(options["output-file"]), timeout) : resolveCommand(parsed).mode === "media-file" ? await downloadProjectMedia(profile, input, String(options["output-file"]), timeout) : resolveCommand(parsed).mode === "upload" ? await uploadLocalMaterial(
9753
10541
  profile,
9754
10542
  String(options.file),
9755
10543
  String(options["request-id"]),
@@ -9758,7 +10546,15 @@ async function runNetworkCli(args, env = process.env, io = {}, executable = "cf"
9758
10546
  ) : await withClient(
9759
10547
  profile,
9760
10548
  timeout,
9761
- (client) => businessCommand(client, parsed, input, profile.resource)
10549
+ (client) => businessCommand(client, parsed, input, profile.resource),
10550
+ {
10551
+ onVersion: (version) => {
10552
+ if (version.compatibility.status === "UPGRADE_RECOMMENDED")
10553
+ (io.notify ?? ((message) => process.stderr.write(message + "\n")))(
10554
+ `The service recommends Native Marketing ${version.compatibility.server.version}. Use update check.`
10555
+ );
10556
+ }
10557
+ }
9762
10558
  );
9763
10559
  return success(command5, data);
9764
10560
  } catch (error) {
@@ -9770,14 +10566,19 @@ function success(command5, data) {
9770
10566
  }
9771
10567
  export {
9772
10568
  CliError as NativeAgentClientError,
10569
+ assessNativeCompatibility,
9773
10570
  callNativeCapability,
9774
10571
  callNativeTool,
10572
+ checkNativeUpdates,
10573
+ defaultUpdatePolicy,
9775
10574
  describeNativeCapability,
9776
10575
  findAgentCapabilities,
9777
10576
  findNativeCapabilities,
9778
10577
  getNativeBusinessSummary,
10578
+ getNativeClientVersion,
9779
10579
  inspectNativeTool,
9780
10580
  listNativeToolsPage,
10581
+ planNativeUpdate,
9781
10582
  projectMediaFileQuerySchema,
9782
10583
  readNativeBusinessPage,
9783
10584
  readNativeCapability,