@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/http.js +270 -25
- package/dist/index.js +854 -53
- package/package.json +11 -1
- package/release.json +2 -2
- package/types/contracts/client-lifecycle.d.ts +55 -0
- package/types/contracts/client-update-policy.d.ts +84 -0
- package/types/contracts/generated/client-release.d.ts +14 -0
- package/types/contracts/index.d.ts +2 -0
- package/types/http/client-version.d.ts +35 -0
- package/types/http/client.d.ts +1 -0
- package/types/http/index.d.ts +2 -0
- package/types/sdk/capability-definition.d.ts +1 -0
- package/types/sdk/cli/run.d.ts +1 -0
- package/types/sdk/client.d.ts +5 -0
- package/types/sdk/index.d.ts +3 -0
- package/types/sdk/transport.d.ts +6 -1
- package/types/sdk/updates/commands.d.ts +11 -0
- package/types/sdk/updates/install.d.ts +20 -0
- package/types/sdk/updates/registry.d.ts +26 -0
- package/types/sdk/updates/store.d.ts +14 -0
- package/types/sdk/updates/version.d.ts +93 -0
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: "
|
|
7997
|
-
{
|
|
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) =>
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
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((
|
|
9158
|
-
resolveCode =
|
|
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((
|
|
9525
|
+
await new Promise((resolve2, reject) => {
|
|
9190
9526
|
server.once("error", reject);
|
|
9191
|
-
server.listen(0, "127.0.0.1",
|
|
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((
|
|
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 =
|
|
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,
|