@cargo-ai/cli 1.0.122 → 1.0.124

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/build/api.d.ts CHANGED
@@ -10,5 +10,11 @@ export declare function createApi(opts: {
10
10
  */
11
11
  getAccessToken: AccessTokenProvider | undefined;
12
12
  workspaceUuid: string | undefined;
13
+ /**
14
+ * Names the command in `x-cargo-origin-command`, for the requests the
15
+ * backend cannot attribute from the route: a dry run reads the same public
16
+ * routes as any catalog lookup and creates no run.
17
+ */
18
+ command?: string;
13
19
  }): Api;
14
20
  //# sourceMappingURL=api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,KAAK,mBAAmB,EAAqB,MAAM,mBAAmB,CAAC;AAIhF,YAAY,EAAE,GAAG,EAAE,CAAC;AAQpB,wBAAgB,SAAS,CAAC,IAAI,EAAE;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,cAAc,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAChD,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,GAAG,GAAG,CAeN"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,KAAK,mBAAmB,EAAqB,MAAM,mBAAmB,CAAC;AAIhF,YAAY,EAAE,GAAG,EAAE,CAAC;AAQpB,wBAAgB,SAAS,CAAC,IAAI,EAAE;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,cAAc,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAChD,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,GAAG,CAeN"}
package/build/api.js CHANGED
@@ -10,7 +10,7 @@ export function createApi(opts) {
10
10
  baseUrl: opts.baseUrl,
11
11
  workspaceUuid: opts.workspaceUuid,
12
12
  getAccessToken: opts.getAccessToken,
13
- origin: ORIGIN,
13
+ origin: { ...ORIGIN, command: opts.command },
14
14
  // A saved session outlives the workspace list baked into its token, so a
15
15
  // workspace granted since signing in reads as a 401 until the token is
16
16
  // reissued. Renew and retry rather than telling the user to sign in again.
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiKzC,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,GACrB,IAAI,CAyEN"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+KzC,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,GACrB,IAAI,CAgFN"}
@@ -4,7 +4,7 @@ import { createApi } from "../api.js";
4
4
  import { getConfig } from "../config.js";
5
5
  import { getCredentialsPath } from "../credentials.js";
6
6
  import { collectSetupIssues, readSkillsPin } from "../setupIssues.js";
7
- import { fetchLatestVersion, isOutdated, UPDATE_COMMAND } from "../version.js";
7
+ import { fetchLatestSkillsPin, fetchLatestVersion, isOutdated, UPDATE_COMMAND, } from "../version.js";
8
8
  import { colors, ExitCodes, outputJson } from "./runHandler.js";
9
9
  const LOGIN_COMMAND = "cargo-ai login --oauth";
10
10
  const STATUS_PAGE = "https://status.getcargo.io";
@@ -26,7 +26,7 @@ function buildCliCheck(currentVersion, latestVersion) {
26
26
  upToDate: true,
27
27
  };
28
28
  }
29
- function buildSkillsPinCheck(currentVersion) {
29
+ function buildSkillsPinCheck(currentVersion, latestSkillsPin) {
30
30
  const pin = readSkillsPin();
31
31
  if (pin === undefined) {
32
32
  return { found: false };
@@ -39,17 +39,22 @@ function buildSkillsPinCheck(currentVersion) {
39
39
  matchesCli: true,
40
40
  };
41
41
  }
42
+ // Whichever side is behind is the side to update: an older CLI cannot run
43
+ // what the skills ask of it, and older skills describe commands this CLI
44
+ // has moved on from. Stale skills only get a fix once a newer bundle is
45
+ // published; before that, `skills add` reinstalls the same pin.
46
+ const cliBehind = isOutdated(currentVersion, pin.version);
47
+ const newerSkills = latestSkillsPin !== undefined && isOutdated(pin.version, latestSkillsPin);
42
48
  return {
43
49
  found: true,
44
50
  pinned: pin.version,
45
51
  path: pin.path,
46
52
  matchesCli: false,
47
- // Whichever side is behind is the side to update: an older CLI cannot run
48
- // what the skills ask of it, and older skills describe commands this CLI
49
- // has moved on from.
50
- fix: isOutdated(currentVersion, pin.version)
51
- ? UPDATE_COMMAND
52
- : "npx skills add getcargohq/cargo-skills",
53
+ ...(cliBehind === true
54
+ ? { fix: UPDATE_COMMAND }
55
+ : newerSkills === true
56
+ ? { fix: "npx skills add getcargohq/cargo-skills" }
57
+ : {}),
53
58
  };
54
59
  }
55
60
  async function checkApi(config) {
@@ -138,8 +143,15 @@ Examples:
138
143
  catch {
139
144
  latestVersion = undefined;
140
145
  }
146
+ let latestSkillsPin;
147
+ try {
148
+ latestSkillsPin = await fetchLatestSkillsPin();
149
+ }
150
+ catch {
151
+ latestSkillsPin = undefined;
152
+ }
141
153
  const cli = buildCliCheck(currentVersion, latestVersion);
142
- const skillsPin = buildSkillsPinCheck(currentVersion);
154
+ const skillsPin = buildSkillsPinCheck(currentVersion, latestSkillsPin);
143
155
  const config = getConfig();
144
156
  const credentials = {
145
157
  source: config.source,
@@ -154,6 +166,7 @@ Examples:
154
166
  const offline = collectSetupIssues({
155
167
  currentVersion,
156
168
  latestVersion,
169
+ latestSkillsPin,
157
170
  });
158
171
  const apiIssue = describeApiIssue(api);
159
172
  const issues = apiIssue === undefined
@@ -1,4 +1,6 @@
1
1
  import type { Command } from "commander";
2
2
  import type { Api } from "../../api.js";
3
- export declare function registerActionCommands(parent: Command, getApi: () => Api, getPublicApi: () => Api): void;
3
+ export declare const ACTION_LIST_COMMAND = "orchestration.action.list";
4
+ export declare const DRY_RUN_COMMAND = "orchestration.action.execute.dry-run";
5
+ export declare function registerActionCommands(parent: Command, getApi: () => Api, getPublicApi: (command?: string) => Api): void;
4
6
  //# sourceMappingURL=action.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/action.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAuBxC,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,EACjB,YAAY,EAAE,MAAM,GAAG,GACtB,IAAI,CA6UN"}
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/action.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AA0BxC,eAAO,MAAM,mBAAmB,8BAA8B,CAAC;AAC/D,eAAO,MAAM,eAAe,yCAAyC,CAAC;AAEtE,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,EACjB,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,GAAG,GACtC,IAAI,CA6UN"}
@@ -4,6 +4,11 @@ import { parseBooleanOption } from "../../utils/booleanOption.js";
4
4
  import { describesOutput } from "../../utils/sampleFromSchema.js";
5
5
  import { applyRecordsLimit, ExitCodes, failWith, handleApiCall, info, outputJson, parseJson, parsePositiveInt, pollUntilFinished, } from "../runHandler.js";
6
6
  import { noteSignedOut, SIGNED_OUT_NOTICE } from "../signedOutNotice.js";
7
+ // Sent as `x-cargo-origin-command` on the public reads these two make, so the
8
+ // backend can count agents browsing the catalog and dry-running an action
9
+ // before (or without) signing up. Neither leaves a run behind to count.
10
+ export const ACTION_LIST_COMMAND = "orchestration.action.list";
11
+ export const DRY_RUN_COMMAND = "orchestration.action.execute.dry-run";
7
12
  export function registerActionCommands(parent, getApi, getPublicApi) {
8
13
  const action = parent
9
14
  .command("action")
@@ -71,7 +76,7 @@ Examples:
71
76
  : parsePositiveInt(opts.limit, "--limit");
72
77
  const query = queryTerms.length === 0 ? undefined : queryTerms.join(" ");
73
78
  noteSignedOut();
74
- const result = await handleApiCall(() => getPublicApi().orchestration.action.list({
79
+ const result = await handleApiCall(() => getPublicApi(ACTION_LIST_COMMAND).orchestration.action.list({
75
80
  query,
76
81
  kind,
77
82
  integrationSlug: opts.integrationSlug,
@@ -204,7 +209,7 @@ Examples:
204
209
  */
205
210
  async function dryRunAction(getPublicApi, actionPayload, data) {
206
211
  const target = dryRunTarget(actionPayload);
207
- const api = getPublicApi();
212
+ const api = getPublicApi(DRY_RUN_COMMAND);
208
213
  const label = target.kind === "connector"
209
214
  ? `${target.integrationSlug}.${target.actionSlug}`
210
215
  : `native.${target.actionSlug}`;
@@ -1,4 +1,4 @@
1
1
  import type { Command } from "commander";
2
2
  import type { Api } from "../../api.js";
3
- export declare function registerOrchestrationCommands(parent: Command, getApi: () => Api, getPublicApi: () => Api): void;
3
+ export declare function registerOrchestrationCommands(parent: Command, getApi: () => Api, getPublicApi: (command?: string) => Api): void;
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAexC,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,EACjB,YAAY,EAAE,MAAM,GAAG,GACtB,IAAI,CA0BN"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/orchestration/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAexC,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,EACjB,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,GAAG,GACtC,IAAI,CA0BN"}
package/build/index.js CHANGED
@@ -34,7 +34,7 @@ import { registerWorkspaceManagementCommands } from "./commands/workspaceManagem
34
34
  import { getConfig } from "./config.js";
35
35
  import { buildPaletteLayout } from "./paletteLayout.js";
36
36
  import { collectSetupIssues, isSignedIn } from "./setupIssues.js";
37
- import { cachedLatestVersion, maybeRefreshUpdateCache, } from "./updateNotifier.js";
37
+ import { cachedLatestSkillsPin, cachedLatestVersion, maybeRefreshUpdateCache, } from "./updateNotifier.js";
38
38
  const require = createRequire(import.meta.url);
39
39
  const { version } = require("../package.json");
40
40
  const program = new Command();
@@ -127,6 +127,7 @@ program.hook("preAction", (_program, action) => {
127
127
  printSetupIssues(collectSetupIssues({
128
128
  currentVersion: version,
129
129
  latestVersion: cachedLatestVersion(),
130
+ latestSkillsPin: cachedLatestSkillsPin(),
130
131
  }));
131
132
  });
132
133
  /** The top-level group a subcommand belongs to (`connection`, not `list`). */
@@ -150,9 +151,9 @@ const getApi = () => {
150
151
  // up. Signed in, it still sends the token, which
151
152
  // is what keeps a workspace's own custom integrations in what it returns.
152
153
  // Every other command keeps `getApi`, which refuses to run without a token.
153
- const getPublicApi = () => {
154
+ const getPublicApi = (command) => {
154
155
  const { baseUrl, getAccessToken, workspaceUuid } = getConfig();
155
- return createApi({ baseUrl, getAccessToken, workspaceUuid });
156
+ return createApi({ baseUrl, getAccessToken, workspaceUuid, command });
156
157
  };
157
158
  // Where the palette finds answers for an option that wants an id. Keyed by the
158
159
  // flag, because `--model-uuid` asks the same question in all 28 places it
@@ -297,7 +298,13 @@ async function openPalette(startPath) {
297
298
  // Probed once: this shells out to `which` per candidate agent, and nobody
298
299
  // installs a coding agent from inside the palette.
299
300
  const agents = availableCodingAgents();
300
- const collectIssues = () => collectSetupIssues({ currentVersion: version, latestVersion });
301
+ // Read after the refresh above, which fetches the skills pin alongside.
302
+ const latestSkillsPin = cachedLatestSkillsPin();
303
+ const collectIssues = () => collectSetupIssues({
304
+ currentVersion: version,
305
+ latestVersion,
306
+ latestSkillsPin,
307
+ });
301
308
  // Re-read after each command: signing in or out from inside the palette
302
309
  // changes both what the setup line says and which rows are worth pinning.
303
310
  // The cwd is re-tested too — a command can create a project in it.
@@ -11,6 +11,14 @@ export type SetupIssueInput = {
11
11
  * palette and the pre-command header pass the cache; `doctor` fetches live.
12
12
  */
13
13
  latestVersion?: string;
14
+ /**
15
+ * The pin of the latest published skills bundle, from the same cache (or a
16
+ * live fetch in `doctor`). Skills that are older than this CLI are only
17
+ * worth reporting when it is newer than the installed pin: the bundle is
18
+ * released after the CLI, so until it is, `skills add` installs the same
19
+ * stale pin and the warning has nothing to offer.
20
+ */
21
+ latestSkillsPin?: string;
14
22
  };
15
23
  /**
16
24
  * The two facts this reads off the machine. Injected so the rules — which side
@@ -1 +1 @@
1
- {"version":3,"file":"setupIssues.d.ts","sourceRoot":"","sources":["../src/setupIssues.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EACL,UAAU,EACV,aAAa,EAEd,MAAM,mBAAmB,CAAC;AAM3B,YAAY,EAAE,SAAS,EAAE,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC;AAErC,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,aAAa,EAAE,MAAM,SAAS,GAAG,SAAS,CAAC;CAC5C,CAAC;AAIF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,eAAe,EACtB,IAAI,GAAE,cAA4B,GACjC,UAAU,EAAE,CAyCd"}
1
+ {"version":3,"file":"setupIssues.d.ts","sourceRoot":"","sources":["../src/setupIssues.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EACL,UAAU,EACV,aAAa,EAEd,MAAM,mBAAmB,CAAC;AAM3B,YAAY,EAAE,SAAS,EAAE,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC;AAErC,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,aAAa,EAAE,MAAM,SAAS,GAAG,SAAS,CAAC;CAC5C,CAAC;AAIF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,eAAe,EACtB,IAAI,GAAE,cAA4B,GACjC,UAAU,EAAE,CAgDd"}
@@ -32,13 +32,19 @@ export function collectSetupIssues(input, deps = machineDeps) {
32
32
  // The pin records the CLI the installed skills were written against. Which
33
33
  // side is behind decides the fix: an older CLI cannot run what the skills
34
34
  // ask of it, and older skills describe commands this CLI has moved on from.
35
- issues.push({
36
- key: "skills-pin-mismatch",
37
- label: `skills expect CLI ${pin.version}`,
38
- fix: isOutdated(input.currentVersion, pin.version) === true
39
- ? UPDATE_COMMAND
40
- : SKILLS_INSTALL_COMMAND,
41
- });
35
+ // Stale skills are only reported once a newer bundle is published: every
36
+ // CLI release ships before its skills, and a fix that reinstalls the same
37
+ // pin is a warning nobody can clear.
38
+ const cliBehind = isOutdated(input.currentVersion, pin.version) === true;
39
+ const newerSkills = input.latestSkillsPin !== undefined &&
40
+ isOutdated(pin.version, input.latestSkillsPin) === true;
41
+ if (cliBehind === true || newerSkills === true) {
42
+ issues.push({
43
+ key: "skills-pin-mismatch",
44
+ label: `skills expect CLI ${pin.version}`,
45
+ fix: cliBehind === true ? UPDATE_COMMAND : SKILLS_INSTALL_COMMAND,
46
+ });
47
+ }
42
48
  }
43
49
  return issues;
44
50
  }
@@ -4,6 +4,12 @@
4
4
  * then simply omits the outdated-CLI issue rather than guessing.
5
5
  */
6
6
  export declare function cachedLatestVersion(): string | undefined;
7
+ /**
8
+ * Pin of the latest published skills bundle, from the same cache. No network.
9
+ * Undefined when it was never fetched: the header then cannot tell whether
10
+ * stale skills have anything to update to, and says nothing.
11
+ */
12
+ export declare function cachedLatestSkillsPin(): string | undefined;
7
13
  /**
8
14
  * Refresh the registry cache at most once per 24h. Returns the latest version
9
15
  * we now know about, so a caller that is about to render setup issues (the
@@ -1 +1 @@
1
- {"version":3,"file":"updateNotifier.d.ts","sourceRoot":"","sources":["../src/updateNotifier.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAYxD;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAwB3E"}
1
+ {"version":3,"file":"updateNotifier.d.ts","sourceRoot":"","sources":["../src/updateNotifier.ts"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAYxD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,SAAS,CAY1D;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAwC3E"}
@@ -1,4 +1,4 @@
1
- import { fetchLatestVersion, readUpdateCache, writeUpdateCache, } from "./version.js";
1
+ import { fetchLatestSkillsPin, fetchLatestVersion, readUpdateCache, writeUpdateCache, } from "./version.js";
2
2
  const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // once per day
3
3
  const NOTIFY_FETCH_TIMEOUT_MS = 1500;
4
4
  const isDisabled = () => {
@@ -20,6 +20,21 @@ export function cachedLatestVersion() {
20
20
  }
21
21
  return cache.latestVersion;
22
22
  }
23
+ /**
24
+ * Pin of the latest published skills bundle, from the same cache. No network.
25
+ * Undefined when it was never fetched: the header then cannot tell whether
26
+ * stale skills have anything to update to, and says nothing.
27
+ */
28
+ export function cachedLatestSkillsPin() {
29
+ if (isDisabled() === true) {
30
+ return undefined;
31
+ }
32
+ const cache = readUpdateCache();
33
+ if (cache === undefined) {
34
+ return undefined;
35
+ }
36
+ return cache.latestSkillsPin;
37
+ }
23
38
  /**
24
39
  * Refresh the registry cache at most once per 24h. Returns the latest version
25
40
  * we now know about, so a caller that is about to render setup issues (the
@@ -41,8 +56,23 @@ export async function maybeRefreshUpdateCache() {
41
56
  let cache = readUpdateCache();
42
57
  const now = Date.now();
43
58
  if (cache === undefined || now - cache.lastCheck > CHECK_INTERVAL_MS) {
44
- const latestVersion = await fetchLatestVersion(NOTIFY_FETCH_TIMEOUT_MS);
45
- cache = { lastCheck: now, latestVersion };
59
+ // The skills pin is a nicety: its failure must not lose the CLI version,
60
+ // nor the pin an earlier check already knew, since the next try is a day
61
+ // away.
62
+ const [latestVersion, fetchedSkillsPin] = await Promise.all([
63
+ fetchLatestVersion(NOTIFY_FETCH_TIMEOUT_MS),
64
+ fetchLatestSkillsPin(NOTIFY_FETCH_TIMEOUT_MS).catch(() => undefined),
65
+ ]);
66
+ const latestSkillsPin = fetchedSkillsPin !== undefined
67
+ ? fetchedSkillsPin
68
+ : cache !== undefined
69
+ ? cache.latestSkillsPin
70
+ : undefined;
71
+ cache = {
72
+ lastCheck: now,
73
+ latestVersion,
74
+ ...(latestSkillsPin !== undefined ? { latestSkillsPin } : {}),
75
+ };
46
76
  writeUpdateCache(cache);
47
77
  }
48
78
  return cache.latestVersion;
@@ -4,6 +4,11 @@ export type UpdateCache = {
4
4
  lastCheck: number;
5
5
  /** Latest version reported by the npm registry at that time. */
6
6
  latestVersion: string;
7
+ /**
8
+ * The pin of the latest published skills bundle at that time. Absent in
9
+ * caches written before it existed, and when that fetch failed.
10
+ */
11
+ latestSkillsPin?: string;
7
12
  };
8
13
  /**
9
14
  * Fetch the latest published version of the CLI from the npm registry.
@@ -11,6 +16,11 @@ export type UpdateCache = {
11
16
  * decide whether to surface or swallow the failure.
12
17
  */
13
18
  export declare function fetchLatestVersion(timeoutMs?: number): Promise<string>;
19
+ /**
20
+ * Fetch the CLI version the latest published skills bundle is pinned to.
21
+ * Throws on network error, timeout, or a body that is not a version.
22
+ */
23
+ export declare function fetchLatestSkillsPin(timeoutMs?: number): Promise<string>;
14
24
  /**
15
25
  * Compare two semver-ish strings. Returns 1 if `a > b`, -1 if `a < b`, 0 if
16
26
  * equal. Only the major.minor.patch core is compared numerically; a version
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,cAAc,wCAAwC,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG;IACxB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,GAAE,MAAiC,GAC3C,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA2B1D;AAED,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,eAAe,IAAI,WAAW,GAAG,SAAS,CAsBzD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAOzD"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,cAAc,wCAAwC,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG;IACxB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,GAAE,MAAiC,GAC3C,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,GAAE,MAAiC,GAC3C,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA2B1D;AAED,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,eAAe,IAAI,WAAW,GAAG,SAAS,CAyBzD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAOzD"}
package/build/version.js CHANGED
@@ -2,6 +2,9 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { getConfigDir } from "./credentials.js";
4
4
  const NPM_REGISTRY_URL = "https://registry.npmjs.org/@cargo-ai/cli/latest";
5
+ // The CLI version the latest published skills bundle was written against: the
6
+ // same file `skills add` installs as the local pin.
7
+ const SKILLS_PIN_URL = "https://raw.githubusercontent.com/getcargohq/cargo-skills/main/cargo/cli-version";
5
8
  const DEFAULT_FETCH_TIMEOUT_MS = 1500;
6
9
  const UPDATE_CACHE_FILE = join(getConfigDir(), "update-check.json");
7
10
  export const UPDATE_COMMAND = "npm install -g @cargo-ai/cli@latest";
@@ -24,6 +27,23 @@ export async function fetchLatestVersion(timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
24
27
  }
25
28
  return body.version;
26
29
  }
30
+ /**
31
+ * Fetch the CLI version the latest published skills bundle is pinned to.
32
+ * Throws on network error, timeout, or a body that is not a version.
33
+ */
34
+ export async function fetchLatestSkillsPin(timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
35
+ const response = await fetch(SKILLS_PIN_URL, {
36
+ signal: AbortSignal.timeout(timeoutMs),
37
+ });
38
+ if (response.ok === false) {
39
+ throw new Error(`skills pin fetch returned HTTP ${response.status}`);
40
+ }
41
+ const pin = (await response.text()).trim();
42
+ if (/^\d+\.\d+\.\d+/.test(pin) === false) {
43
+ throw new Error("skills pin response is not a version");
44
+ }
45
+ return pin;
46
+ }
27
47
  /**
28
48
  * Compare two semver-ish strings. Returns 1 if `a > b`, -1 if `a < b`, 0 if
29
49
  * equal. Only the major.minor.patch core is compared numerically; a version
@@ -72,6 +92,9 @@ export function readUpdateCache() {
72
92
  return {
73
93
  lastCheck: parsed.lastCheck,
74
94
  latestVersion: parsed.latestVersion,
95
+ ...(typeof parsed.latestSkillsPin === "string"
96
+ ? { latestSkillsPin: parsed.latestSkillsPin }
97
+ : {}),
75
98
  };
76
99
  }
77
100
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cargo-ai/cli",
3
- "version": "1.0.122",
3
+ "version": "1.0.124",
4
4
  "mcpName": "io.github.getcargohq/cargo",
5
5
  "private": false,
6
6
  "license": "UNLICENSED",
@@ -72,12 +72,12 @@
72
72
  "format:check": "prettier --check ."
73
73
  },
74
74
  "dependencies": {
75
- "@ai-sdk/harness": "^1.0.144",
76
- "@ai-sdk/harness-claude-code": "^1.0.148",
75
+ "@ai-sdk/harness": "1.0.144",
76
+ "@ai-sdk/harness-claude-code": "1.0.148",
77
77
  "@cargo-ai/api": "^1.0.101",
78
78
  "@cargo-ai/app-sdk": "^1.0.11",
79
- "@cargo-ai/cdk": "^1.0.117",
80
- "@cargo-ai/types": "^1.0.101",
79
+ "@cargo-ai/cdk": "^1.0.119",
80
+ "@cargo-ai/types": "^1.0.102",
81
81
  "@cargo-ai/utils": "^1.0.35",
82
82
  "@cargo-ai/worker-sdk": "^1.0.21",
83
83
  "@modelcontextprotocol/sdk": "^1.31.0",
@@ -86,7 +86,7 @@
86
86
  "commander": "^12.1.0",
87
87
  "jsonschema": "^1.4.1",
88
88
  "tsx": "^4.19.2",
89
- "undici": "^7.29.1",
89
+ "undici": "^7.30.1",
90
90
  "uuid": "^14.0.0"
91
91
  },
92
92
  "devDependencies": {