@wrangled/cli 0.3.150 → 0.3.151
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/api.js +30 -2
- package/dist/guide.js +1 -0
- package/dist/main.js +31 -7
- package/dist/version-check.js +28 -0
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { brand, envPrefix } from "./brand.js";
|
|
5
5
|
import { RUN_ROOT_ENV, writeFileAtomic } from "./files.js";
|
|
6
|
+
import { noteLatestCli } from "./version-check.js";
|
|
6
7
|
export const exitCodes = { ok: 0, error: 1, usage: 2, auth: 3, timedOut: 4, interrupted: 130 };
|
|
7
8
|
export class CliError extends Error {
|
|
8
9
|
exitCode;
|
|
@@ -335,9 +336,9 @@ async function request(connection, path, init) {
|
|
|
335
336
|
});
|
|
336
337
|
}
|
|
337
338
|
catch (error) {
|
|
338
|
-
|
|
339
|
-
throw new CliError(`Cannot reach ${connection.apiUrl}: ${cause}`, exitCodes.error);
|
|
339
|
+
throw cannotReach(connection.apiUrl, error);
|
|
340
340
|
}
|
|
341
|
+
noteLatestCli(response);
|
|
341
342
|
if (response.status === 401) {
|
|
342
343
|
throw new CliError(connection.rejected ?? `The API key was rejected by ${connection.apiUrl}. Run \`${brand.slug} login\` with a valid key.`, exitCodes.auth);
|
|
343
344
|
}
|
|
@@ -347,6 +348,33 @@ async function request(connection, path, init) {
|
|
|
347
348
|
}
|
|
348
349
|
return response;
|
|
349
350
|
}
|
|
351
|
+
/** The build the API at `apiUrl` runs; it needs no key. */
|
|
352
|
+
export async function fetchApiVersion(apiUrl) {
|
|
353
|
+
let response;
|
|
354
|
+
let body;
|
|
355
|
+
try {
|
|
356
|
+
response = await fetch(`${apiUrl}/api/v1/version`, { signal: AbortSignal.timeout(VERSION_TIMEOUT_MS) });
|
|
357
|
+
noteLatestCli(response);
|
|
358
|
+
if (!response.ok)
|
|
359
|
+
throw new CliError(`HTTP ${response.status} from /api/v1/version at ${apiUrl}`, exitCodes.error);
|
|
360
|
+
body = await response.json().catch(() => undefined);
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
if (error instanceof CliError)
|
|
364
|
+
throw error;
|
|
365
|
+
throw cannotReach(apiUrl, error);
|
|
366
|
+
}
|
|
367
|
+
const api = body?.api;
|
|
368
|
+
if (typeof api?.cliLatest !== "string" || (api.commit !== null && typeof api.commit !== "string")) {
|
|
369
|
+
throw new CliError(`/api/v1/version at ${apiUrl} did not answer with a version.`, exitCodes.error);
|
|
370
|
+
}
|
|
371
|
+
return { api: { commit: api.commit, cliLatest: api.cliLatest } };
|
|
372
|
+
}
|
|
373
|
+
const VERSION_TIMEOUT_MS = 5_000;
|
|
374
|
+
function cannotReach(apiUrl, error) {
|
|
375
|
+
const cause = error.cause;
|
|
376
|
+
return new CliError(`Cannot reach ${apiUrl}: ${cause?.message || cause?.code || error.message}`, exitCodes.error);
|
|
377
|
+
}
|
|
350
378
|
function parseErrorBody(text) {
|
|
351
379
|
try {
|
|
352
380
|
return JSON.parse(text).error;
|
package/dist/guide.js
CHANGED
|
@@ -453,6 +453,7 @@ A scheduled agent's run acts as its skill's agent, the way the Facebook ads agen
|
|
|
453
453
|
${cli} login [--client <name>] validate a key (prompted, piped, or ${envPrefix}_API_KEY) and save it, as the default or under a client's name
|
|
454
454
|
${cli} logout [--client <name>] delete the default saved login, or one client's
|
|
455
455
|
${cli} whoami show the current workspace, library access, and which client's key is in use and why
|
|
456
|
+
${cli} version this CLI's version, the commit the API runs, and the latest CLI it ships (also --version); needs no login
|
|
456
457
|
${cli} knowledge search "<question>" [--limit <n>] [--kind <kind>] [--hook-kind <v> ...] short, cited snippets from the know-how libraries (keys with library access)
|
|
457
458
|
${cli} knowledge tag <entry-id> --hook-kind <v> --confidence <c> tag a library entry with the marketing grammar (or --file tags.json)
|
|
458
459
|
${cli} guide what this workspace's data holds, how fresh it is, and what is missing: read it first
|
package/dist/main.js
CHANGED
|
@@ -5,7 +5,7 @@ import { mkdir, readFile, stat } from "node:fs/promises";
|
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
8
|
-
import { api, ApiError, apiDownload, apiText, apiUpload, CliError, exitCodes, getSigned, IMPORT_LIMITS, DOCUMENT_LIMITS, META_IDS_MAX, PROFILE_MESSAGING_KINDS, OUTCOME_PARTS, FACET_WORDS, GOOGLE_ADS_IDS_MAX, IMPORT_NAME_PATTERN, AGENT_TABLE_LIMITS, AGENT_TABLE_NAME_PATTERN, putSigned, CLIENT_FILE, parseClientName, readSavedLogins, removeCredentials, resolveApiUrl, resolveConnection, resolveLogin, UnreadableCredentialsError, writeCredentials, OUTBOUND_PUSH_MAX, entryTagFlags, streamSigned, } from "./api.js";
|
|
8
|
+
import { api, ApiError, apiDownload, apiText, apiUpload, CliError, exitCodes, getSigned, IMPORT_LIMITS, DOCUMENT_LIMITS, META_IDS_MAX, PROFILE_MESSAGING_KINDS, OUTCOME_PARTS, FACET_WORDS, GOOGLE_ADS_IDS_MAX, IMPORT_NAME_PATTERN, AGENT_TABLE_LIMITS, AGENT_TABLE_NAME_PATTERN, putSigned, CLIENT_FILE, parseClientName, readSavedLogins, removeCredentials, resolveApiUrl, chosenApiUrl, fetchApiVersion, resolveConnection, resolveLogin, UnreadableCredentialsError, writeCredentials, OUTBOUND_PUSH_MAX, entryTagFlags, streamSigned, } from "./api.js";
|
|
9
9
|
import { brand, envPrefix } from "./brand.js";
|
|
10
10
|
import { campaignLayout, money, moneyLayout } from "./report-columns.js";
|
|
11
11
|
import { renderDataMap, renderQuestions } from "./map.js";
|
|
@@ -29,6 +29,7 @@ import { AGENT_ENV, HARNESS_COMMAND_ENV, RUNNER_TOKEN_ENV, runLoop, runOnce } fr
|
|
|
29
29
|
import { npmRegistry } from "./runner/self-update.js";
|
|
30
30
|
import { SKILL_NAME_PATTERN } from "./skill-zip.js";
|
|
31
31
|
import { installSkillFolder, operatorConnection, packSkill, parseSkillRef, readSkillFolder, unpackSkill, } from "./skills.js";
|
|
32
|
+
import { ownVersion } from "./version-check.js";
|
|
32
33
|
import { versionAtLeast } from "./version.js";
|
|
33
34
|
const DEFAULT_WAIT_SECONDS = 300;
|
|
34
35
|
const META_AD_PAUSE_USAGE = 'meta ad pause <ad-id> --idempotency-key <key> --reason "<why>" [--name "<expected>"] [--run <run-id>]';
|
|
@@ -114,6 +115,25 @@ const commands = {
|
|
|
114
115
|
console.log(`\n${clientReason(choice)}`);
|
|
115
116
|
},
|
|
116
117
|
},
|
|
118
|
+
version: {
|
|
119
|
+
usage: "version this CLI's version, and the API's: the commit it runs and the latest CLI it ships (also --version)",
|
|
120
|
+
async run(args, options) {
|
|
121
|
+
if (args.length > 0)
|
|
122
|
+
throw new CliError("Usage: version", exitCodes.usage);
|
|
123
|
+
const url = await chosenApiUrl(options);
|
|
124
|
+
const api = await fetchApiVersion(url).then(({ api }) => ({ url, commit: api.commit, cliLatest: api.cliLatest }), (error) => {
|
|
125
|
+
if (!(error instanceof CliError))
|
|
126
|
+
throw error;
|
|
127
|
+
console.error(`error: ${error.message}`);
|
|
128
|
+
return null;
|
|
129
|
+
});
|
|
130
|
+
if (options.format === "json")
|
|
131
|
+
return printJson({ cli: ownVersion(), api });
|
|
132
|
+
console.log(`CLI ${ownVersion()}`);
|
|
133
|
+
if (api)
|
|
134
|
+
console.log(`API ${api.url} commit ${api.commit ?? "unknown"}, latest CLI ${api.cliLatest}`);
|
|
135
|
+
},
|
|
136
|
+
},
|
|
117
137
|
"knowledge search": {
|
|
118
138
|
usage: 'knowledge search ["<question>"] [--limit <n>] [--kind message|transcript|document|note] [--library <slug>] [--angle <v>] [--hook-kind <v>] [--ad-format <v>] [--awareness <v>]',
|
|
119
139
|
async run(args, options) {
|
|
@@ -4033,8 +4053,7 @@ async function withRunner(options, work) {
|
|
|
4033
4053
|
await work({
|
|
4034
4054
|
apiUrl: resolveApiUrl(options.apiUrl, null),
|
|
4035
4055
|
token,
|
|
4036
|
-
|
|
4037
|
-
cliVersion: JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8")).version,
|
|
4056
|
+
cliVersion: ownVersion(),
|
|
4038
4057
|
harnessCommand: process.env[HARNESS_COMMAND_ENV] || undefined,
|
|
4039
4058
|
openRouterKey: process.env[OPENROUTER_KEY_ENV] || undefined,
|
|
4040
4059
|
maxSeconds: options.maxSeconds,
|
|
@@ -5213,11 +5232,14 @@ the runner starts instead of Claude Code or Codex.
|
|
|
5213
5232
|
|
|
5214
5233
|
Run \`${brand.slug} docs\` for the full guide.
|
|
5215
5234
|
`;
|
|
5216
|
-
/**
|
|
5217
|
-
|
|
5235
|
+
/**
|
|
5236
|
+
* A bare --questions (map's switch) or --version (the version command, with no command before it) as an empty value,
|
|
5237
|
+
* since funnels draft gives --questions a value, and funnels show and dashboards rollback --version.
|
|
5238
|
+
*/
|
|
5239
|
+
const bareSwitches = (argv) => argv.map((arg, i) => ((arg === "--questions" || arg === "--version") && (i === argv.length - 1 || argv[i + 1].startsWith("-")) ? `${arg}=` : arg));
|
|
5218
5240
|
function parseOptions(argv) {
|
|
5219
5241
|
const { values, positionals } = parseArgs({
|
|
5220
|
-
args:
|
|
5242
|
+
args: bareSwitches(argv),
|
|
5221
5243
|
allowPositionals: true,
|
|
5222
5244
|
options: {
|
|
5223
5245
|
format: { type: "string", default: "table" },
|
|
@@ -6982,7 +7004,7 @@ async function newerCliHint() {
|
|
|
6982
7004
|
// Tests run the CLI with NODE_ENV=test; they ask a registry only when they name one, so npm's real version cannot change their output.
|
|
6983
7005
|
if (process.env.NODE_ENV === "test" && !process.env.npm_config_registry)
|
|
6984
7006
|
return undefined;
|
|
6985
|
-
const version =
|
|
7007
|
+
const version = ownVersion();
|
|
6986
7008
|
const latest = await npmRegistry.latest(3_000).catch(() => undefined);
|
|
6987
7009
|
if (latest === undefined || versionAtLeast(version, latest))
|
|
6988
7010
|
return undefined;
|
|
@@ -6990,6 +7012,8 @@ async function newerCliHint() {
|
|
|
6990
7012
|
}
|
|
6991
7013
|
async function main(argv) {
|
|
6992
7014
|
const { positionals, options, help: wantsHelp } = parseOptions(argv);
|
|
7015
|
+
if (positionals.length === 0 && options.version === "" && !wantsHelp)
|
|
7016
|
+
return commands.version.run([], options);
|
|
6993
7017
|
if (wantsHelp || positionals.length === 0) {
|
|
6994
7018
|
process.stdout.write(help);
|
|
6995
7019
|
return;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { brand } from "./brand.js";
|
|
3
|
+
import { VERSION_PATTERN, versionAtLeast } from "./version.js";
|
|
4
|
+
let own;
|
|
5
|
+
/** This CLI's version, from the package.json one folder above dist/ (and src/). */
|
|
6
|
+
export function ownVersion() {
|
|
7
|
+
own ??= JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
8
|
+
return own;
|
|
9
|
+
}
|
|
10
|
+
/** The response header that names the latest CLI version the API's build ships. */
|
|
11
|
+
export const CLI_LATEST_HEADER = `x-${brand.slug}-cli-latest`;
|
|
12
|
+
/** The line to warn with when `latest` is a newer version than `version`; nothing when it is not, or is no version at all. */
|
|
13
|
+
export function cliUpdateWarning(version, latest) {
|
|
14
|
+
if (!latest || !VERSION_PATTERN.test(latest) || !VERSION_PATTERN.test(version) || versionAtLeast(version, latest))
|
|
15
|
+
return undefined;
|
|
16
|
+
return `warning: this CLI is ${version} and the API's latest is ${latest}. Update: npm install -g ${brand.npmPackage}@latest`;
|
|
17
|
+
}
|
|
18
|
+
let warned = false;
|
|
19
|
+
/** Warns on stderr, once per process, when an API response says a newer CLI is out. */
|
|
20
|
+
export function noteLatestCli(response) {
|
|
21
|
+
if (warned)
|
|
22
|
+
return;
|
|
23
|
+
const warning = cliUpdateWarning(ownVersion(), response.headers.get(CLI_LATEST_HEADER));
|
|
24
|
+
if (warning === undefined)
|
|
25
|
+
return;
|
|
26
|
+
warned = true;
|
|
27
|
+
process.stderr.write(`${warning}\n`);
|
|
28
|
+
}
|