toolaby 1.8.2 → 1.8.3

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/bin.mjs CHANGED
@@ -44,7 +44,7 @@ import { runMcp } from './lib/mcp.js';
44
44
  import { runCheck } from './lib/check.js';
45
45
  import { inside, signInPage, devicePolling, opener, privateKind, privateContent, privateCopies, googleKey, projectRoot, gitDirectory, gitObject, gitObjects, gitFile, notAnImage, unpacked, COMPRESSED, publicLinks, privateLinks, oneLine, fetchBounded, fetchHere, readBounded, readText, SCAFFOLD_FILES } from './lib/safe.js';
46
46
 
47
- const VERSION = '1.8.2';
47
+ const VERSION = '1.8.3';
48
48
  const CLIENT_ID = 'toolaby-cli';
49
49
  const argv = process.argv.slice(2);
50
50
  // `--version` and `--help` are commands of their own; every other command is the first bare word.
@@ -312,7 +312,7 @@ async function wire() {
312
312
  // The background the manifest names, read from inside the folder only: whether it starts the Wall, or is the Wall's.
313
313
  const namedWorker = workerIn(manifest) ?? '';
314
314
  const workerSource = namedWorker && has(namedWorker) && !inside(dir, namedWorker).why ? readOr(join(dir, namedWorker), '') : null;
315
- const wiring = project.manifestFile && manifest ? wireManifest(manifest, { site, has, remembered, rememberedSidePanel, manifestKey, ownInFront, workerSource }) : null;
315
+ const wiring = project.manifestFile && manifest ? wireManifest(manifest, { site, apex: new URL(wall).host, has, remembered, rememberedSidePanel, manifestKey, ownInFront, workerSource }) : null;
316
316
  const appPopup = wiring ? wiring.appPopup : project.wrapper ? (project.appPopup ?? remembered) : null;
317
317
  const appSidePanel = wiring ? wiring.appSidePanel : project.wrapper ? (project.appSidePanel ?? rememberedSidePanel) : null;
318
318
  const targets = filesToWrite(project, appPopup, appSidePanel, { popup: !!wiring?.ownPopup, sidePanel: !!wiring?.ownSidePanel });
@@ -743,6 +743,15 @@ function pack() {
743
743
  const hosts = manifest.host_permissions ?? []; const ec = manifest.externally_connectable?.matches ?? [];
744
744
  if (!hosts.includes(sitePattern(site)) || !ec.includes(sitePattern(site))) problems.push(`The manifest does not name ${oneLine(sitePattern(site))} in both host_permissions and externally_connectable: sign-in and the purchase would not reach the extension. \`npx -y toolaby@latest wire <tool-id> --live --force\` adds them.`);
745
745
  }
746
+ // A host under the Wall's address that no key in the build names: another workspace's, or the other side's Test
747
+ // address, left by an earlier wiring or pasted by hand. The store build asks its buyers for it at install, and gains
748
+ // nothing: the extension talks to its own key's host alone (30 Sep 2026).
749
+ const walls = [...new Set(['toolaby.app', new URL(wall).hostname])];
750
+ const hostOf = (m) => /^(?:\*|https?|wss?):\/\/([^/]+)\//.exec(m)?.[1]?.replace(/^\*\./, '').toLowerCase() ?? null;
751
+ const keys = new Set([...sites].map(sitePattern));
752
+ const strangers = [...new Set([...(manifest.host_permissions ?? []), ...(manifest.externally_connectable?.matches ?? [])])]
753
+ .filter((m) => typeof m === 'string' && !keys.has(m) && walls.some((w) => { const h = hostOf(m); return !!h && (h === w || h.endsWith(`.${w}`)); }));
754
+ if (strangers.length && !flag('--allow-wall-hosts')) problems.push(`The manifest names ${strangers.map(oneLine).join(', ')}: the Wall's, and no key's in this build — another workspace, or the other side's Test address. A store build asks its buyers for ${strangers.length > 1 ? 'these' : 'it'} at install for nothing: take ${strangers.length > 1 ? 'them' : 'it'} out of host_permissions and externally_connectable. (--allow-wall-hosts packs it anyway.)`);
746
755
  if (problems.length) fail(`Not packed:\n\n${problems.map((p) => ` · ${p}`).join('\n\n')}`);
747
756
 
748
757
  const hadKey = typeof manifest.key === 'string';
@@ -785,7 +794,7 @@ function mcp() {
785
794
  }
786
795
 
787
796
  async function check() {
788
- const passed = await runCheck({ mark: (tone, text) => say(` ${tone === 'ok' ? green('✓') : tone === 'warn' ? yellow('!') : red('✗')} ${plain(text)}`), dir: opt('--into') ?? positional[0] ?? '.', out, api: (path, o = {}) => api(path, o), token: storedToken, detectProject, wireManifest, listTree, readJson, browser: flag('--browser') });
797
+ const passed = await runCheck({ mark: (tone, text) => say(` ${tone === 'ok' ? green('✓') : tone === 'warn' ? yellow('!') : red('✗')} ${plain(text)}`), dir: opt('--into') ?? positional[0] ?? '.', out, api: (path, o = {}) => api(path, o), token: storedToken, detectProject, wireManifest: (m, o) => wireManifest(m, { apex: new URL(wall).host, ...o }), listTree, readJson, browser: flag('--browser') });
789
798
  if (!passed) process.exit(1);
790
799
  }
791
800
 
@@ -804,7 +813,8 @@ function help() {
804
813
  --surfaces both|popup|side-panel|none what it opens (default: both)
805
814
  tools your tools, with their ids
806
815
  pack [folder] the zip for the Chrome Web Store: the built extension, its manifest's "key" left out;
807
- refuses a Test key or localhost inside --out <file.zip>
816
+ refuses a Test key, localhost, or another of the Wall's hosts inside
817
+ --out <file.zip>
808
818
  leaves out keys, certificates, .env and credential files, source maps, secrets,
809
819
  git's files, a build's copy of a private file (compressed, UTF-16 or base64),
810
820
  an image that is none; refuses a build if one is a file the manifest names
package/lib/manifest.js CHANGED
@@ -127,8 +127,11 @@ export function wireManifest(input, o) {
127
127
  const pattern = sitePattern(o.site);
128
128
  // A Test address of the Wall's that is not this key's (./test-address): left by an earlier wiring to the other side.
129
129
  // Test's workspaces have hosts of their own, and a build wired for Test and then for Live went on asking its buyers,
130
- // at install, for access to Test's host too (29 Sep 2026).
131
- const stale = (p) => typeof p === 'string' && p !== pattern && isTestAddress(/^https:\/\/([a-z0-9-]+)\.[^/]+\/\*$/.exec(p)?.[1]);
130
+ // at install, for access to Test's host too (29 Sep 2026). Only under the Wall's own address: a host of the
131
+ // developer's that reads like one, on a domain of theirs, was taken out of their build too (30 Sep 2026).
132
+ const apex = (o.apex ?? host.split('.').slice(1).join('.')).toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
133
+ const underWall = new RegExp(`^https://([a-z0-9-]+)\\.${apex}/\\*$`);
134
+ const stale = (p) => typeof p === 'string' && p !== pattern && isTestAddress(underWall.exec(p)?.[1]);
132
135
  for (const field of ['host_permissions', 'externally_connectable']) {
133
136
  const list = field === 'host_permissions' ? manifest.host_permissions : manifest.externally_connectable?.matches;
134
137
  const gone = Array.isArray(list) ? list.filter(stale) : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolaby",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "The Toolaby command line: sign in once, then wire any Chrome extension to your tool on the Wall — sign-in, the Free plan, licences and subscriptions — or start a new one; check it, and give coding agents the Wall as an MCP server.",
5
5
  "type": "module",
6
6
  "bin": {