grok-bot-cli 0.6.0 → 0.8.0

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/install.mjs CHANGED
@@ -7,7 +7,7 @@ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'nod
7
7
  import { fileURLToPath } from 'node:url';
8
8
 
9
9
  const pluginName = "gbot";
10
- const pluginVersion = "0.6.0";
10
+ const pluginVersion = "0.8.0";
11
11
  const receiptFile = ".agent-bundle-install.json";
12
12
  const receiptFormat = "agent-bundle-install-receipt/2";
13
13
  const legacyReceiptFormat = "agent-bundle-install-receipt/1";
@@ -118,7 +118,7 @@ const hashEntry = (hash, relative, metadata, bytes) => {
118
118
  // Deterministic tree walk: symlinks and special files refused, the root receipt skipped. `transform`
119
119
  // maps a file's bytes to what the Cursor copy will hold (the Agent Plugins mcp.json expansion below), so
120
120
  // the artifact hash describes the installed form and reruns compare like for like.
121
- const inventory = async (root, transform) => {
121
+ const readTree = async (root, transform, selectedPaths) => {
122
122
  const rootMetadata = await lstat(root);
123
123
  if (rootMetadata.isSymbolicLink() || !rootMetadata.isDirectory()) throw unsupported('.');
124
124
  const hash = createHash('sha256');
@@ -139,10 +139,11 @@ const inventory = async (root, transform) => {
139
139
  const bytes = await readFile(absolute);
140
140
  hashEntry(hash, relative, metadata, transform === undefined ? bytes : transform(posixPath, bytes));
141
141
  };
142
- if (artifactManifest !== undefined) {
143
- const selected = new Set(['agent-bundle.manifest.json', ...artifactManifest.files.map((file) => file.path)]);
144
- for (const file of ['.env', '.env.local']) if (await exists(join(root, file))) selected.add(file);
145
- for (const file of [...selected].sort(compareTreePaths)) await visit(file);
142
+ if (selectedPaths !== undefined) {
143
+ for (const file of selectedPaths) {
144
+ if (!(await exists(join(root, file)))) throw new Error(`bundle does not match its manifest: ${file} is missing.`);
145
+ await visit(file);
146
+ }
146
147
  return { files, hash: hash.digest('hex') };
147
148
  }
148
149
  for (const entry of (await readdir(root)).sort((left, right) => left.localeCompare(right))) {
@@ -157,6 +158,13 @@ const inventory = async (root, transform) => {
157
158
  }
158
159
  return { files, hash: hash.digest('hex') };
159
160
  };
161
+ const inventory = (root, transform) => readTree(root, transform, undefined);
162
+ const artifactInventory = async (root, transform) => {
163
+ if (artifactManifest === undefined) return inventory(root, transform);
164
+ const selected = new Set(['agent-bundle.manifest.json', ...artifactManifest.files.map((file) => file.path)]);
165
+ for (const file of ['.env', '.env.local']) if (await exists(join(root, file))) selected.add(file);
166
+ return readTree(root, transform, [...selected].sort(compareTreePaths));
167
+ };
160
168
 
161
169
  const treeHash = async (root) => (await inventory(root)).hash;
162
170
 
@@ -545,7 +553,7 @@ const stage = async (tree, receiptOptions = {}) => {
545
553
  };
546
554
  await cp(source, root, { errorOnExist: true, filter, force: false, recursive: true, verbatimSymlinks: true });
547
555
  if (expansion !== undefined) await writeFile(join(root, expansion.mcpDocument), expansion.expanded, 'utf8');
548
- const staged = await inventory(root);
556
+ const staged = await artifactInventory(root);
549
557
  await writeFile(join(root, receiptFile), receiptFor(staged, receiptOptions), 'utf8');
550
558
  return { inventory: staged, parent, root };
551
559
  } catch (error) {
@@ -1018,7 +1026,7 @@ const marketplaceManifestPath = join(marketplaceRepo, '.cursor-plugin', 'marketp
1018
1026
  // commit lets --uninstall prove the repository is still the one staging wrote.
1019
1027
  const writeMarketplaceReceipt = async (commit) => {
1020
1028
  const previous = await readReceiptFile(marketplaceReceipt);
1021
- const tree = await inventory(source);
1029
+ const tree = await artifactInventory(source);
1022
1030
  if (previous !== undefined && previous.contentHash === tree.hash && previous.registrations[0]?.commit === commit) return;
1023
1031
  await mkdir(receiptsRoot, { recursive: true });
1024
1032
  // The committed repository is removed wholesale after a HEAD check; the receipt owns no individual files.
@@ -1081,6 +1089,7 @@ if (mode === 'marketplace') {
1081
1089
  if (nestedGit !== undefined) {
1082
1090
  throw new Error(`--mode marketplace refuses bundle-internal Git metadata at ${JSON.stringify(nestedGit)}: git would record it as an empty gitlink and Cursor would import a plugin without files. Stage from a built bundle directory without .git, or use the default local mode.`);
1083
1091
  }
1092
+ await artifactInventory(source);
1084
1093
  await mkdir(marketplaceRoot, { recursive: true });
1085
1094
  if (await exists(marketplaceRepo)) {
1086
1095
  let stagedVersion;
@@ -1089,6 +1098,7 @@ if (mode === 'marketplace') {
1089
1098
  if (stagedVersion !== undefined && stagedVersion !== pluginVersion) {
1090
1099
  throw new Error(`Refusing version collision at ${marketplaceRepo}: found ${stagedVersion}, requested ${pluginVersion}.`);
1091
1100
  }
1101
+ // The staged plugin is an unfiltered copy of the bundle, so both sides hash the files present.
1092
1102
  if (await exists(marketplacePlugin) && await exists(join(marketplaceRepo, '.git')) && await treeHash(source) === await treeHash(marketplacePlugin)) {
1093
1103
  let stagedManifest;
1094
1104
  try { stagedManifest = await readFile(marketplaceManifestPath, 'utf8'); }
@@ -1143,7 +1153,7 @@ if (mode === 'marketplace') {
1143
1153
  }
1144
1154
 
1145
1155
  // The artifact is inventoried (and any unsupported entry refused) before anything is created in the home.
1146
- const artifact = await inventory(source, cursorTransform);
1156
+ const artifact = await artifactInventory(source, cursorTransform);
1147
1157
  // The receipt records which host directories this run creates on the way to the plugin root (a fresh Cursor
1148
1158
  // home has no plugins/local), so --uninstall can prune exactly those and no more.
1149
1159
  const createdHostDirectories = [];