toolaby 1.7.0 → 1.7.2
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/README.md +5 -3
- package/bin.mjs +41 -34
- package/lib/summary.js +8 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
The Toolaby command line. It puts the Wall — sign-in, a Free plan, paid plans, licences and subscriptions — into a Chrome extension you already have, or makes a new one for you.
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npm i -g toolaby
|
|
7
|
-
toolaby wire <tool-id> # in your extension's folder, on Test; signs you in first if it must
|
|
8
|
-
toolaby wire <tool-id> --live # the same, on Live: the store build
|
|
6
|
+
npm i -g toolaby # once; or run every command through `npx -y toolaby@latest …`
|
|
7
|
+
toolaby-cli wire <tool-id> # in your extension's folder, on Test; signs you in first if it must
|
|
8
|
+
toolaby-cli wire <tool-id> --live # the same, on Live: the store build
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
The command is `toolaby-cli`: on Windows, the Command Prompt runs a `toolaby.js` in the current folder before a command named `toolaby`, and every wired folder has one. Below, `toolaby <command>` names each command.
|
|
12
|
+
|
|
11
13
|
In a repository you did not write, use the installed command: `npx` can run a `toolaby` the project carries, or fetch one from a registry the project's `.npmrc` names.
|
|
12
14
|
|
|
13
15
|
Every command acts on the test Wall (`test.toolaby.app`) unless told `--live` (`toolaby.app`), as Stripe's command line does. `toolaby tools` lists your ids on either side.
|
package/bin.mjs
CHANGED
|
@@ -38,13 +38,13 @@ import { homedir } from 'node:os';
|
|
|
38
38
|
import { spawn } from 'node:child_process';
|
|
39
39
|
import { createHmac, randomBytes } from 'node:crypto';
|
|
40
40
|
import { wireManifest, drawsItself, WORKER_LINES, wallWorkerSource, configValues, wrappedBy, namedWorker as workerIn, startsPlatform as startsWall, CLASSIC_CLIENT } from './lib/manifest.js';
|
|
41
|
-
import { wiringSummary, popupSwapIn, ownCode } from './lib/summary.js';
|
|
41
|
+
import { wiringSummary, popupSwapIn, gatesIn, ownCode } from './lib/summary.js';
|
|
42
42
|
import { detectProject, CODE_FILES, POPUP_FILES, SIDE_PANEL_FILES, filesToWrite, fillPages, scaffoldManifest, scaffoldFilesFor, surfacesOf, hasPopup, hasSidePanel, SURFACES, STALE_FILES, BUILD_COPY_NOTE, AGENT_FILES, AGENT_ENTRY_FILES } from './lib/project.js';
|
|
43
43
|
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.7.
|
|
47
|
+
const VERSION = '1.7.2';
|
|
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.
|
|
@@ -167,11 +167,11 @@ async function login() {
|
|
|
167
167
|
}
|
|
168
168
|
const why = poll.json.error;
|
|
169
169
|
if (why === 'slow_down') every += 5000;
|
|
170
|
-
else if (why === 'expired_token') fail('The code expired before it was approved. Run `toolaby login` again.');
|
|
170
|
+
else if (why === 'expired_token') fail('The code expired before it was approved. Run `npx -y toolaby@latest login` again.');
|
|
171
171
|
else if (why === 'access_denied') fail('The sign-in was denied in the browser. Nothing was signed in.');
|
|
172
172
|
else if (why && why !== 'authorization_pending') fail(`The Wall answered "${oneLine(why)}"${poll.json.error_description ? `: ${oneLine(poll.json.error_description)}` : ''}.`);
|
|
173
173
|
}
|
|
174
|
-
fail('The code expired before it was approved. Run `toolaby login` again.');
|
|
174
|
+
fail('The code expired before it was approved. Run `npx -y toolaby@latest login` again.');
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
/** A token to act with: the stored one, else a sign-in right here — `wire` in a fresh terminal is one command, not two. */
|
|
@@ -183,9 +183,9 @@ async function tokenOrLogin() {
|
|
|
183
183
|
|
|
184
184
|
async function whoami() {
|
|
185
185
|
const token = storedToken();
|
|
186
|
-
if (!token) fail(`Not signed in to ${side} (${wall}). Run \`toolaby login${live ? ' --live' : ''}\`.${envElsewhere()}`);
|
|
186
|
+
if (!token) fail(`Not signed in to ${side} (${wall}). Run \`npx -y toolaby@latest login${live ? ' --live' : ''}\`.${envElsewhere()}`);
|
|
187
187
|
const me = await api('/api/cli/me', { token });
|
|
188
|
-
if (me.status === 401) fail(`The session for ${wall} is no longer valid. Run \`toolaby login\`.`);
|
|
188
|
+
if (me.status === 401) fail(`The session for ${wall} is no longer valid. Run \`npx -y toolaby@latest login\`.`);
|
|
189
189
|
if (!me.ok) fail(`The Wall answered HTTP ${me.status}.`);
|
|
190
190
|
out(`\n ${oneLine(me.json.email)} · ${side} · ${wall}\n`);
|
|
191
191
|
for (const w of me.json.workspaces) out(` ${oneLine(w.name)} (${oneLine(w.slug)}, ${oneLine(w.role)}) ${oneLine(w.site)}`);
|
|
@@ -207,7 +207,7 @@ async function logout() {
|
|
|
207
207
|
async function tools() {
|
|
208
208
|
const token = await tokenOrLogin();
|
|
209
209
|
const res = await api('/api/cli/tools', { token });
|
|
210
|
-
if (res.status === 401) fail(`The session for ${side} is no longer valid. Run \`toolaby login${live ? ' --live' : ''}\`.`);
|
|
210
|
+
if (res.status === 401) fail(`The session for ${side} is no longer valid. Run \`npx -y toolaby@latest login${live ? ' --live' : ''}\`.`);
|
|
211
211
|
if (!res.ok) fail(`The Wall answered HTTP ${res.status}.`);
|
|
212
212
|
if (!res.json.tools.length) { out(`\n No tools on ${side} yet. Make one in the dashboard, then \`toolaby wire <tool-id>${live ? ' --live' : ''}\` in your extension's folder.\n`); return; }
|
|
213
213
|
out(`\n ${side}\n`);
|
|
@@ -276,7 +276,7 @@ async function filesFor(slug, scaffold, manifestKey = null, surfaces = null) {
|
|
|
276
276
|
|
|
277
277
|
async function wire() {
|
|
278
278
|
const slug = positional[0];
|
|
279
|
-
if (!isId(slug)) fail(command === 'upgrade' ? 'usage: toolaby upgrade <tool-id> [--into <folder>] — `toolaby tools` lists your ids' : 'usage: toolaby wire <tool-id> [--into <folder>] [--force] [--adopt-key] — `toolaby tools` lists your ids');
|
|
279
|
+
if (!isId(slug)) fail(command === 'upgrade' ? 'usage: npx -y toolaby@latest upgrade <tool-id> [--into <folder>] — `npx -y toolaby@latest tools` lists your ids' : 'usage: npx -y toolaby@latest wire <tool-id> [--into <folder>] [--force] [--adopt-key] — `npx -y toolaby@latest tools` lists your ids');
|
|
280
280
|
const dir = resolve(opt('--into') ?? '.');
|
|
281
281
|
if (!existsSync(dir)) fail(`${dir} does not exist`);
|
|
282
282
|
const tree = listTree(dir, 3);
|
|
@@ -297,9 +297,16 @@ async function wire() {
|
|
|
297
297
|
const oldConfig = existsSync(configPath) ? readOr(configPath, '') : '';
|
|
298
298
|
const remembered = oldConfig.match(/appPopup:\s*'([^']*)'/)?.[1] || null;
|
|
299
299
|
const rememberedSidePanel = oldConfig.match(/appSidePanel:\s*'([^']*)'/)?.[1] || null;
|
|
300
|
+
// The extension's own code, read first: a popup it handles itself (setPopup, getPopup) decides which page stands in
|
|
301
|
+
// front, and whether it opens the paywall anywhere decides whether the lines for it are a step.
|
|
302
|
+
const code = project.manifestFile ? [...tree].filter(ownCode).slice(0, 400).map((path) => { let text = ''; try { if (statSync(join(dir, path)).size < 512_000) text = readOr(join(dir, path), ''); } catch { /* gone */ } return { path, text }; }) : [];
|
|
303
|
+
const popupSwap = popupSwapIn(code);
|
|
304
|
+
// Wired for the first time, an extension whose code handles its popup keeps it in front: the Wall's in front of it is
|
|
305
|
+
// skipped by setPopup(), and found by getPopup() where the code expects its own (Chrome's action sample threw so).
|
|
306
|
+
const keptForSwap = !oldConfig && !!popupSwap && typeof manifest?.action?.default_popup === 'string' && !flag('--own-popup');
|
|
300
307
|
// An extension whose own pages draw the panel — what `create` makes, "your popup first", or asked with --own-popup —
|
|
301
308
|
// keeps them in front.
|
|
302
|
-
const ownInFront = flag('--own-popup') || drawsItself(manifest, oldConfig);
|
|
309
|
+
const ownInFront = flag('--own-popup') || drawsItself(manifest, oldConfig) || keptForSwap;
|
|
303
310
|
// The background the manifest names, read from inside the folder only: whether it starts the Wall, or is the Wall's.
|
|
304
311
|
const namedWorker = workerIn(manifest) ?? '';
|
|
305
312
|
const workerSource = namedWorker && has(namedWorker) && !inside(dir, namedWorker).why ? readOr(join(dir, namedWorker), '') : null;
|
|
@@ -344,9 +351,7 @@ async function wire() {
|
|
|
344
351
|
} else if (project.createWorker) {
|
|
345
352
|
write(project.createWorker.file, project.createWorker.content);
|
|
346
353
|
}
|
|
347
|
-
|
|
348
|
-
const code = appPopup ? [...tree].filter(ownCode).slice(0, 400).map((path) => { let text = ''; try { if (statSync(join(dir, path)).size < 512_000) text = readOr(join(dir, path), ''); } catch { /* gone */ } return { path, text }; }) : [];
|
|
349
|
-
const summary = wiringSummary({ project, wiring, appPopup, appSidePanel, workerStarts: !!project.worker && startsPlatform(join(dir, project.worker)), popupSwap: popupSwapIn(code) });
|
|
354
|
+
const summary = wiringSummary({ project, wiring, appPopup, appSidePanel, workerStarts: !!project.worker && startsPlatform(join(dir, project.worker)), popupSwap, keptForSwap, gates: project.manifestFile ? gatesIn(code) : null });
|
|
350
355
|
|
|
351
356
|
// Wired again, a folder takes `upgrade` (wire, replacing the platform's files): the command, ready to paste.
|
|
352
357
|
const into = opt('--into');
|
|
@@ -366,7 +371,7 @@ async function wire() {
|
|
|
366
371
|
|
|
367
372
|
async function create() {
|
|
368
373
|
const slug = positional[0];
|
|
369
|
-
if (!isId(slug)) fail('usage: toolaby create <tool-id> [--dir <folder>] — `toolaby tools` lists your ids');
|
|
374
|
+
if (!isId(slug)) fail('usage: npx -y toolaby@latest create <tool-id> [--dir <folder>] — `npx -y toolaby@latest tools` lists your ids');
|
|
370
375
|
const dir = resolve(opt('--dir') ?? slug);
|
|
371
376
|
if (there(dir)) fail(`${dir} already exists`);
|
|
372
377
|
// Which surfaces this extension is made with, asked first: the files for coding agents name only what is written.
|
|
@@ -437,7 +442,7 @@ function devSecret() {
|
|
|
437
442
|
}
|
|
438
443
|
async function catalogue() {
|
|
439
444
|
const res = await api('/api/cli/webhooks/events', { token: await tokenOrLogin() });
|
|
440
|
-
if (res.status === 401) fail(`The session for ${side} is no longer valid. Run \`toolaby login${live ? ' --live' : ''}\`.`);
|
|
445
|
+
if (res.status === 401) fail(`The session for ${side} is no longer valid. Run \`npx -y toolaby@latest login${live ? ' --live' : ''}\`.`);
|
|
441
446
|
if (!res.ok) fail(`The Wall answered HTTP ${res.status}.`);
|
|
442
447
|
return res.json.events;
|
|
443
448
|
}
|
|
@@ -457,7 +462,7 @@ async function webhooks() {
|
|
|
457
462
|
}
|
|
458
463
|
if (sub === 'trigger') {
|
|
459
464
|
const type = positional[1];
|
|
460
|
-
if (!type) fail('usage: toolaby webhooks trigger <event> [--to <url>] [--secret <whsec_…>] — `toolaby webhooks events` lists them');
|
|
465
|
+
if (!type) fail('usage: npx -y toolaby@latest webhooks trigger <event> [--to <url>] [--secret <whsec_…>] — `npx -y toolaby@latest webhooks events` lists them');
|
|
461
466
|
const to = opt('--to');
|
|
462
467
|
if (!to) {
|
|
463
468
|
const res = await api('/api/cli/webhooks/trigger', { method: 'POST', body: { type, workspace: opt('--workspace') ?? undefined }, token: await tokenOrLogin() });
|
|
@@ -467,7 +472,7 @@ async function webhooks() {
|
|
|
467
472
|
}
|
|
468
473
|
const events = await catalogue();
|
|
469
474
|
const event = events.find((e) => e.type === type);
|
|
470
|
-
if (!event) fail(`No event "${type}". \`toolaby webhooks events\` lists them.`);
|
|
475
|
+
if (!event) fail(`No event "${type}". \`npx -y toolaby@latest webhooks events\` lists them.`);
|
|
471
476
|
const secret = opt('--secret') ?? devSecret();
|
|
472
477
|
if (!/^whsec_[A-Za-z0-9+/=]+$/.test(secret)) fail('--secret must be a Standard Webhooks secret: whsec_ followed by base64.');
|
|
473
478
|
const id = `msg_${randomBytes(12).toString('base64url')}`;
|
|
@@ -489,14 +494,14 @@ async function webhooks() {
|
|
|
489
494
|
const res = await api(`/api/cli/webhooks/endpoints${ws ? `?workspace=${encodeURIComponent(ws)}` : ''}`, { token: await tokenOrLogin() });
|
|
490
495
|
if (!res.ok) fail(oneLine(res.json.message ?? `The Wall answered HTTP ${res.status}.`));
|
|
491
496
|
out(`\n ${oneLine(res.json.workspace)} · ${side}\n`);
|
|
492
|
-
if (res.json.endpoints.length === 0) out(' None yet. `toolaby webhooks add <url>` registers one.');
|
|
497
|
+
if (res.json.endpoints.length === 0) out(' None yet. `npx -y toolaby@latest webhooks add <url>` registers one.');
|
|
493
498
|
for (const e of res.json.endpoints) out(` ${oneLine(e.id)} ${e.disabled ? 'paused ' : 'on '} ${oneLine(e.url)}${e.description ? ` — ${oneLine(e.description)}` : ''}${e.events.length ? ` (${oneLine(e.events.join(', '))})` : ''}`);
|
|
494
499
|
out();
|
|
495
500
|
return;
|
|
496
501
|
}
|
|
497
502
|
if (sub === 'add') {
|
|
498
503
|
const url = positional[1];
|
|
499
|
-
if (!url) fail('usage: toolaby webhooks add <url> [--description <text>] [--events a.b,c.d] [--workspace <slug>]');
|
|
504
|
+
if (!url) fail('usage: npx -y toolaby@latest webhooks add <url> [--description <text>] [--events a.b,c.d] [--workspace <slug>]');
|
|
500
505
|
const events = (opt('--events') ?? '').split(',').map((e) => e.trim()).filter(Boolean);
|
|
501
506
|
const res = await api('/api/cli/webhooks/endpoints', { method: 'POST', body: { url, description: opt('--description') ?? '', events, workspace: opt('--workspace') ?? undefined }, token: await tokenOrLogin() });
|
|
502
507
|
if (!res.ok) fail(oneLine(res.json.message ?? `The Wall answered HTTP ${res.status}.`));
|
|
@@ -505,13 +510,13 @@ async function webhooks() {
|
|
|
505
510
|
}
|
|
506
511
|
if (sub === 'remove') {
|
|
507
512
|
const id = positional[1];
|
|
508
|
-
if (!id) fail('usage: toolaby webhooks remove <ep_…> [--workspace <slug>] — `toolaby webhooks endpoints` lists them');
|
|
513
|
+
if (!id) fail('usage: npx -y toolaby@latest webhooks remove <ep_…> [--workspace <slug>] — `npx -y toolaby@latest webhooks endpoints` lists them');
|
|
509
514
|
const res = await api('/api/cli/webhooks/endpoints', { method: 'DELETE', body: { id, workspace: opt('--workspace') ?? undefined }, token: await tokenOrLogin() });
|
|
510
515
|
if (!res.ok) fail(oneLine(res.json.message ?? `The Wall answered HTTP ${res.status}.`));
|
|
511
516
|
out(`\n Removed ${oneLine(res.json.removed)} · ${oneLine(res.json.workspace)} · ${side}. Deliveries to it stop at once.\n`);
|
|
512
517
|
return;
|
|
513
518
|
}
|
|
514
|
-
fail('usage: toolaby webhooks events | trigger <event> [--to <url>] | endpoints | add <url> | remove <id>');
|
|
519
|
+
fail('usage: npx -y toolaby@latest webhooks events | trigger <event> [--to <url>] | endpoints | add <url> | remove <id>');
|
|
515
520
|
}
|
|
516
521
|
|
|
517
522
|
// ── pack: the zip the Chrome Web Store takes ──────────────────────────────────────────────────────────────────────────
|
|
@@ -779,35 +784,37 @@ function help() {
|
|
|
779
784
|
out(`
|
|
780
785
|
toolaby — the Toolaby command line (${VERSION})
|
|
781
786
|
|
|
782
|
-
|
|
783
|
-
|
|
787
|
+
npx -y toolaby@latest <command>, or installed (npm i -g toolaby): toolaby-cli <command>
|
|
788
|
+
|
|
789
|
+
wire <tool-id> wire the extension in this folder to your tool, on Test
|
|
790
|
+
wire <tool-id> --live the same, on Live: the store build
|
|
784
791
|
--into <folder> another folder --force replace the platform's files
|
|
785
792
|
--own-popup keep your popup in front (the Wall's opens from toolaby.showPaywall())
|
|
786
|
-
|
|
787
|
-
|
|
793
|
+
upgrade <tool-id> the platform's files replaced with the current ones — the popup, the client — yours untouched
|
|
794
|
+
create <tool-id> a new, minimal extension for your tool, in <tool-id>/ --dir <folder>
|
|
788
795
|
--surfaces both|popup|side-panel|none what it opens (default: both)
|
|
789
|
-
|
|
790
|
-
|
|
796
|
+
tools your tools, with their ids
|
|
797
|
+
pack [folder] the zip for the Chrome Web Store: the built extension, its manifest's "key" left out;
|
|
791
798
|
refuses a Test key or localhost inside --out <file.zip>
|
|
792
799
|
leaves out keys, certificates, .env and credential files, source maps, secrets,
|
|
793
800
|
git's files, a build's copy of a private file (compressed, UTF-16 or base64),
|
|
794
801
|
an image that is none; refuses a build if one is a file the manifest names
|
|
795
802
|
--allow-private packs them
|
|
796
|
-
|
|
803
|
+
check [folder] whether the extension here is wired right: files, key, manifest, client, features;
|
|
797
804
|
--browser loads it in Chromium (needs Playwright in the project)
|
|
798
|
-
|
|
805
|
+
mcp the Wall for coding agents, an MCP server on stdio:
|
|
799
806
|
claude mcp add toolaby -- npx -y toolaby@latest mcp
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
807
|
+
webhooks events the six events, with an example of each
|
|
808
|
+
webhooks trigger <event> an example delivery to your endpoints; --to <url> sends it to a local server instead
|
|
809
|
+
webhooks endpoints your endpoints; add <url> registers one (secret shown once), remove <id> removes one
|
|
810
|
+
login · whoami · logout the session, per side; a command that needs one signs you in itself
|
|
804
811
|
|
|
805
812
|
Every command acts on Test (test.toolaby.app) unless told --live (toolaby.app).
|
|
806
813
|
--wall <url> or TOOLABY_WALL names any other Wall; TOOLABY_TOKEN a session, for CI —
|
|
807
814
|
sent to Live alone, or to the one Wall TOOLABY_TOKEN_WALL names (https://test.toolaby.app for Test).
|
|
808
815
|
|
|
809
|
-
|
|
810
|
-
|
|
816
|
+
In a repository you did not write, install it (npm i -g toolaby) and type toolaby-cli: npx can run a toolaby
|
|
817
|
+
the project carries, or fetch one from a registry its .npmrc names.
|
|
811
818
|
`);
|
|
812
819
|
}
|
|
813
820
|
|
package/lib/summary.js
CHANGED
|
@@ -9,6 +9,8 @@ export function popupSwapIn(files) {
|
|
|
9
9
|
}
|
|
10
10
|
return null;
|
|
11
11
|
}
|
|
12
|
+
/** Whether the extension's own code opens the paywall anywhere: gate() or showPaywall(), outside its comments. */
|
|
13
|
+
export const gatesIn = (files) => files.some((f) => /\btoolaby\s*\.\s*(gate|showPaywall)\s*\(/.test(withoutComments(f.text)));
|
|
12
14
|
/** Whether a path is the extension's own code, for popupSwapIn: a script, not the Wall's, not a dependency's or a build's. */
|
|
13
15
|
export const ownCode = (path) => /\.(m?js|jsx|ts|tsx)$/.test(path) && !/(^|\/)toolaby[^/]*$/.test(path) && !/(^|\/)(node_modules|dist|build|\.output|\.wxt|\.plasmo)\//.test(path);
|
|
14
16
|
export function wiringSummary(o) {
|
|
@@ -21,6 +23,8 @@ export function wiringSummary(o) {
|
|
|
21
23
|
? { tone: 'warn', label: 'Popup', text: `Your code handles the popup itself (${o.popupSwap.call} in ${o.popupSwap.file}), so keep yours in front`, fix: 'own-popup' }
|
|
22
24
|
: { tone: 'ok', label: 'Popup', text: `The Wall checks first, then opens ${o.appPopup}` });
|
|
23
25
|
}
|
|
26
|
+
else if (ownPopup && o.keptForSwap && o.popupSwap)
|
|
27
|
+
rows.push({ tone: 'ok', label: 'Popup', text: `Yours stays in front, as your code handles it (${o.popupSwap.call} in ${o.popupSwap.file})` });
|
|
24
28
|
else if (ownPopup)
|
|
25
29
|
rows.push({ tone: 'ok', label: 'Popup', text: `Yours stays in front; toolaby.showPaywall() opens the Wall's` });
|
|
26
30
|
else if (p.note)
|
|
@@ -55,6 +59,10 @@ export function wiringSummary(o) {
|
|
|
55
59
|
rows.push({ tone: 'todo', label: 'Background', text: `Add these 2 lines to the top of ${p.worker}:`, code: p.workerLines.trim() });
|
|
56
60
|
else if (p.worker)
|
|
57
61
|
rows.push({ tone: 'ok', label: 'Background', text: `${p.worker} starts the Wall` });
|
|
62
|
+
// With no page of the Wall's in front, the paywall opens only where the code asks for it: when nothing does, the lines.
|
|
63
|
+
if (!o.appPopup && !o.appSidePanel && o.gates === false && p.kind === 'plain') {
|
|
64
|
+
rows.push({ tone: 'todo', label: 'Paywall', text: p.built ? 'Nothing opens it yet. Where your paid action starts, add (the import is the path to toolaby.js):' : 'Nothing opens it yet. Where your paid action starts, in a module script, add:', code: `import { toolaby } from '${p.built ? './toolaby.js' : '/toolaby.js'}';\n\nconst permit = await toolaby.gate();\nif (!permit.allowed) return toolaby.showPaywall(permit);` });
|
|
65
|
+
}
|
|
58
66
|
// The manifest: what it gained, or, generated by a framework, what its config needs.
|
|
59
67
|
if (w)
|
|
60
68
|
rows.push({ tone: 'ok', label: 'Manifest', text: w.added.length ? w.added.join(' · ') : 'Has what the Wall needs' });
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolaby",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
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": {
|
|
7
|
-
"toolaby": "bin.mjs"
|
|
7
|
+
"toolaby-cli": "bin.mjs"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin.mjs",
|