@bitpub/cli 2.0.0 → 2.0.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/package.json +1 -1
- package/src/commands/init.js +5 -0
- package/src/commands/setup.js +71 -0
- package/src/commands/update.js +50 -77
- package/src/commands/welcome.js +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitpub/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "BitPub CLI — local-first shared memory for AI agents. Six daily verbs (save/load/list/find/sync/delete), zero-config private namespace, encrypted client-side.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bitpub": "./bin/bitpub.js"
|
package/src/commands/init.js
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
ensureIdentity,
|
|
16
16
|
ensureWorkspace,
|
|
17
17
|
ensureSkill,
|
|
18
|
+
runFirstRunWelcome,
|
|
18
19
|
} = require('./setup');
|
|
19
20
|
const { DEFAULT_CLOUD_URL } = require('../config');
|
|
20
21
|
|
|
@@ -27,6 +28,7 @@ module.exports = function registerInit(program) {
|
|
|
27
28
|
.option('--no-workspace', 'Skip creating a .bitpub/workspace.json in the current folder')
|
|
28
29
|
.option('--workspace-label <string>', 'Override the workspace label (defaults to folder name)')
|
|
29
30
|
.option('--force', 'Overwrite an existing identity (the old key is unrecoverable)')
|
|
31
|
+
.option('--no-welcome', 'Skip the post-install welcome (save first memory + open browser)')
|
|
30
32
|
.option('--import-from <path>', 'Import an existing TollBit config')
|
|
31
33
|
.option('--no-import', 'Skip auto-import')
|
|
32
34
|
.action(async (opts) => {
|
|
@@ -41,6 +43,9 @@ module.exports = function registerInit(program) {
|
|
|
41
43
|
ensureWorkspace({ owner: config.owner, label: opts.workspaceLabel });
|
|
42
44
|
}
|
|
43
45
|
await ensureSkill({});
|
|
46
|
+
if (config && !opts.localOnly && opts.welcome !== false) {
|
|
47
|
+
await runFirstRunWelcome(config);
|
|
48
|
+
}
|
|
44
49
|
} catch (err) {
|
|
45
50
|
console.error(`\n✗ ${err.message}`);
|
|
46
51
|
process.exit(1);
|
package/src/commands/setup.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
const path = require('path');
|
|
24
24
|
const fs = require('fs');
|
|
25
25
|
const os = require('os');
|
|
26
|
+
const { spawn } = require('child_process');
|
|
26
27
|
const axios = require('axios');
|
|
27
28
|
const {
|
|
28
29
|
readConfig,
|
|
@@ -169,6 +170,65 @@ async function ensureSkill({ quiet = false } = {}) {
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
/**
|
|
174
|
+
* First-run welcome: save the Welcome slice and (if interactive) open the
|
|
175
|
+
* browser to view it. Shared by `bitpub setup` and the deprecated
|
|
176
|
+
* `bitpub init` so any install path lands the user in the same place.
|
|
177
|
+
*
|
|
178
|
+
* Idempotent through welcome.js's `welcome_saved_at` config flag. The
|
|
179
|
+
* browser tab only opens on a *fresh* save (so re-running setup doesn't
|
|
180
|
+
* pop new tabs forever); when the slice already exists we just print a
|
|
181
|
+
* pointer to `bitpub browser`.
|
|
182
|
+
*
|
|
183
|
+
* Auto-open is gated:
|
|
184
|
+
* - `BITPUB_SKIP_WELCOME=1` → skip
|
|
185
|
+
* - non-TTY stdout (CI, headless agents w/o pty) → skip with a pointer
|
|
186
|
+
* - otherwise → spawn `bitpub welcome --serve` detached so the server
|
|
187
|
+
* outlives this process and the user gets their shell back.
|
|
188
|
+
*/
|
|
189
|
+
async function runFirstRunWelcome(config) {
|
|
190
|
+
const { saveWelcomeSlice } = require('./welcome');
|
|
191
|
+
|
|
192
|
+
let result;
|
|
193
|
+
try {
|
|
194
|
+
result = await saveWelcomeSlice(config, {});
|
|
195
|
+
} catch (err) {
|
|
196
|
+
console.error(`\n(welcome: could not save first memory — ${err.message})`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Re-run on an already-welcomed machine — quietly skip the browser
|
|
201
|
+
// open so a `bitpub setup` re-invocation isn't disruptive.
|
|
202
|
+
if (!result.saved) return;
|
|
203
|
+
|
|
204
|
+
console.log(`\n✓ Saved your first memory → ${result.address} (v${result.version})`);
|
|
205
|
+
|
|
206
|
+
const skipReason = welcomeSkipReason();
|
|
207
|
+
if (skipReason) {
|
|
208
|
+
console.log(` (skipping auto browser open: ${skipReason})`);
|
|
209
|
+
console.log(' Run `bitpub welcome --serve` to open it when you\'re ready.');
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const child = spawn(process.execPath, [process.argv[1], 'welcome', '--serve'], {
|
|
215
|
+
detached: true,
|
|
216
|
+
stdio: 'ignore',
|
|
217
|
+
});
|
|
218
|
+
child.unref();
|
|
219
|
+
console.log(' Opening your browser at http://localhost:4141 …');
|
|
220
|
+
} catch (err) {
|
|
221
|
+
console.error(` (could not auto-open browser: ${err.message})`);
|
|
222
|
+
console.error(' Run `bitpub welcome --serve` manually.');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function welcomeSkipReason() {
|
|
227
|
+
if (process.env.BITPUB_SKIP_WELCOME === '1') return 'BITPUB_SKIP_WELCOME=1';
|
|
228
|
+
if (!process.stdout.isTTY) return 'non-interactive shell';
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
172
232
|
module.exports = function registerSetup(program) {
|
|
173
233
|
const setup = program
|
|
174
234
|
.command('setup')
|
|
@@ -178,6 +238,7 @@ module.exports = function registerSetup(program) {
|
|
|
178
238
|
.option('--no-anchor', 'Skip anchoring this folder as a project')
|
|
179
239
|
.option('--label <string>', 'Override the project label (defaults to folder name)')
|
|
180
240
|
.option('--force', 'Overwrite an existing identity (the old key is unrecoverable after this)')
|
|
241
|
+
.option('--no-welcome', 'Skip the post-install welcome (save first memory + open browser)')
|
|
181
242
|
.option('--import-from <path>', 'Import an existing TollBit config (defaults to ~/.tollbit/config.json if present)')
|
|
182
243
|
.option('--no-import', 'Skip auto-import even if a legacy ~/.tollbit/ config is present')
|
|
183
244
|
.action(async (opts) => {
|
|
@@ -191,6 +252,15 @@ module.exports = function registerSetup(program) {
|
|
|
191
252
|
ensureWorkspace({ owner: config.owner, label: opts.label });
|
|
192
253
|
}
|
|
193
254
|
await ensureSkill({});
|
|
255
|
+
|
|
256
|
+
// Welcome flow: save a first memory + open the browser. Idempotent
|
|
257
|
+
// and gated — re-running `bitpub setup` doesn't keep popping tabs.
|
|
258
|
+
// Skipped in --local-only (no cloud identity to write to) and when
|
|
259
|
+
// the user opted out via --no-welcome or BITPUB_SKIP_WELCOME=1.
|
|
260
|
+
if (config && !opts.localOnly && opts.welcome !== false) {
|
|
261
|
+
await runFirstRunWelcome(config);
|
|
262
|
+
}
|
|
263
|
+
|
|
194
264
|
console.log('\nTry it:');
|
|
195
265
|
console.log(' bitpub save notes "first slice"');
|
|
196
266
|
console.log(' bitpub list # see what\'s in this project');
|
|
@@ -310,3 +380,4 @@ function maybeImportLegacyConfig({ quiet = false } = {}) {
|
|
|
310
380
|
module.exports.ensureIdentity = ensureIdentity;
|
|
311
381
|
module.exports.ensureWorkspace = ensureWorkspace;
|
|
312
382
|
module.exports.ensureSkill = ensureSkill;
|
|
383
|
+
module.exports.runFirstRunWelcome = runFirstRunWelcome;
|
package/src/commands/update.js
CHANGED
|
@@ -1,103 +1,102 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* `bitpub update` — pull the latest CLI
|
|
4
|
+
* `bitpub update` — pull the latest CLI from the npm registry and
|
|
5
5
|
* reinstall globally.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Source of truth is the public npm package at
|
|
8
|
+
* https://www.npmjs.com/package/@bitpub/cli
|
|
9
|
+
* which is published from cli/package.json. We read the latest version
|
|
10
|
+
* via the registry API (https://registry.npmjs.org/@bitpub/cli/latest)
|
|
11
|
+
* and then delegate the actual install to `npm install -g @bitpub/cli@<version>`.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Anyone with the CLI on PATH can also self-update without this command —
|
|
14
|
+
* `npm install -g @bitpub/cli@latest` is equivalent. `bitpub update` exists
|
|
15
|
+
* to add (1) a one-line version diff before installing, (2) automatic
|
|
16
|
+
* refresh of installed skill files after the install, and (3) a `--check`
|
|
17
|
+
* mode for CI / agent flows that want to know "is there a newer version"
|
|
18
|
+
* without actually upgrading.
|
|
19
|
+
*
|
|
20
|
+
* `--registry <url>` overrides the registry, for teams publishing to a
|
|
21
|
+
* private npm registry (Verdaccio, GitHub Packages, AWS CodeArtifact, etc.).
|
|
16
22
|
*/
|
|
17
23
|
|
|
18
|
-
const path = require('path');
|
|
19
|
-
const fs = require('fs');
|
|
20
|
-
const os = require('os');
|
|
21
24
|
const { spawnSync } = require('child_process');
|
|
22
25
|
const axios = require('axios');
|
|
23
|
-
const { readConfig } = require('../config');
|
|
24
26
|
const { refreshExistingSkills } = require('./skills');
|
|
25
27
|
|
|
28
|
+
const PACKAGE_NAME = '@bitpub/cli';
|
|
29
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
|
30
|
+
const NPM_WEB_URL = `https://www.npmjs.com/package/${PACKAGE_NAME}`;
|
|
31
|
+
|
|
26
32
|
module.exports = function registerUpdate(program) {
|
|
27
33
|
program
|
|
28
34
|
.command('update')
|
|
29
|
-
.description('Update the BitPub CLI (and any installed skill files) to the latest
|
|
30
|
-
.option('--
|
|
35
|
+
.description('Update the BitPub CLI (and any installed skill files) to the latest version on npm')
|
|
36
|
+
.option('--registry <url>', 'Override the npm registry (for private npm mirrors)')
|
|
31
37
|
.option('--check', 'Only check the remote version; do not install')
|
|
32
38
|
.option('--skip-skills', 'Do not refresh installed skill files after the update')
|
|
33
39
|
.action(async (opts) => {
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
opts.url ||
|
|
37
|
-
process.env.BITPUB_CLOUD_URL ||
|
|
38
|
-
config?.api_url ||
|
|
39
|
-
'https://bitpub.io'
|
|
40
|
-
).replace(/\/$/, '');
|
|
41
|
-
|
|
42
|
-
const tarballUrl = `${cloudUrl}/cli/latest.tgz`;
|
|
40
|
+
const registry = (opts.registry || DEFAULT_REGISTRY).replace(/\/$/, '');
|
|
41
|
+
const versionUrl = `${registry}/${encodeURIComponent(PACKAGE_NAME)}/latest`;
|
|
43
42
|
const localVersion = require('../../package.json').version;
|
|
44
43
|
|
|
45
44
|
console.log(`Current : v${localVersion}`);
|
|
46
|
-
console.log(`
|
|
47
|
-
|
|
48
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bitpub-update-'));
|
|
49
|
-
const tarballPath = path.join(tmpDir, 'bitpub-cli.tgz');
|
|
45
|
+
console.log(`Source : ${NPM_WEB_URL}`);
|
|
46
|
+
if (opts.registry) console.log(`Registry: ${registry}`);
|
|
50
47
|
|
|
48
|
+
let remoteVersion;
|
|
51
49
|
try {
|
|
52
|
-
const resp = await axios.get(
|
|
53
|
-
|
|
54
|
-
timeout: 30_000,
|
|
55
|
-
maxRedirects: 5,
|
|
50
|
+
const resp = await axios.get(versionUrl, {
|
|
51
|
+
timeout: 15_000,
|
|
56
52
|
validateStatus: (s) => s === 200,
|
|
53
|
+
headers: { accept: 'application/json' },
|
|
57
54
|
});
|
|
58
|
-
|
|
55
|
+
remoteVersion = resp.data?.version;
|
|
59
56
|
} catch (err) {
|
|
60
|
-
cleanup(tmpDir);
|
|
61
57
|
const status = err.response?.status;
|
|
62
|
-
if (status) {
|
|
63
|
-
console.error(`\n✗
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
58
|
+
if (status === 404) {
|
|
59
|
+
console.error(`\n✗ ${PACKAGE_NAME} not found on ${registry}.`);
|
|
60
|
+
console.error(' If you publish to a private registry, pass --registry <url>.');
|
|
61
|
+
} else if (status) {
|
|
62
|
+
console.error(`\n✗ Registry query failed: HTTP ${status} from ${versionUrl}`);
|
|
68
63
|
} else {
|
|
69
|
-
console.error(`\n✗
|
|
64
|
+
console.error(`\n✗ Registry query failed: ${err.message}`);
|
|
70
65
|
}
|
|
71
66
|
process.exit(1);
|
|
72
67
|
}
|
|
73
68
|
|
|
74
|
-
|
|
69
|
+
if (!remoteVersion) {
|
|
70
|
+
console.error(`\n✗ Registry response did not include a version. URL: ${versionUrl}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
75
74
|
console.log(`Remote : v${remoteVersion}`);
|
|
76
75
|
|
|
77
76
|
if (opts.check) {
|
|
78
|
-
cleanup(tmpDir);
|
|
79
77
|
if (remoteVersion === localVersion) {
|
|
80
78
|
console.log('\n✓ Already up to date.');
|
|
81
79
|
} else {
|
|
82
|
-
console.log(
|
|
80
|
+
console.log(`\nRun \`bitpub update\` to install v${remoteVersion}.`);
|
|
83
81
|
}
|
|
84
82
|
return;
|
|
85
83
|
}
|
|
86
84
|
|
|
87
85
|
if (remoteVersion === localVersion) {
|
|
88
|
-
console.log(
|
|
86
|
+
console.log(`\nAlready on v${localVersion} — reinstalling anyway to refresh files.`);
|
|
89
87
|
}
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
});
|
|
89
|
+
const installSpec = `${PACKAGE_NAME}@${remoteVersion}`;
|
|
90
|
+
const npmArgs = ['install', '-g', installSpec];
|
|
91
|
+
if (opts.registry) npmArgs.push('--registry', registry);
|
|
95
92
|
|
|
96
|
-
|
|
93
|
+
console.log(`\nInstalling: npm ${npmArgs.join(' ')}`);
|
|
94
|
+
const result = spawnSync('npm', npmArgs, { stdio: 'inherit' });
|
|
97
95
|
|
|
98
96
|
if (result.status !== 0) {
|
|
99
97
|
console.error('\n✗ npm install -g failed.');
|
|
100
98
|
console.error(' Try running the command with sudo, or set npm prefix to a writable dir.');
|
|
99
|
+
console.error(` Or manually: npm install -g ${installSpec}`);
|
|
101
100
|
process.exit(result.status || 1);
|
|
102
101
|
}
|
|
103
102
|
|
|
@@ -112,7 +111,7 @@ module.exports = function registerUpdate(program) {
|
|
|
112
111
|
return;
|
|
113
112
|
}
|
|
114
113
|
if (updates.length === 0) {
|
|
115
|
-
console.log(' (no skill installs found — run `bitpub
|
|
114
|
+
console.log(' (no skill installs found — run `bitpub setup skill install` to set them up)');
|
|
116
115
|
return;
|
|
117
116
|
}
|
|
118
117
|
for (const u of updates) {
|
|
@@ -127,29 +126,3 @@ module.exports = function registerUpdate(program) {
|
|
|
127
126
|
}
|
|
128
127
|
});
|
|
129
128
|
};
|
|
130
|
-
|
|
131
|
-
function cleanup(tmpDir) {
|
|
132
|
-
try {
|
|
133
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
134
|
-
} catch {
|
|
135
|
-
// best-effort
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Peek at `package/package.json` inside an npm tarball using the system
|
|
141
|
-
* `tar`. Both BSD tar (macOS default) and GNU tar (Linux) support
|
|
142
|
-
* `-xzOf <file> <member>` to extract a single entry to stdout. Returns
|
|
143
|
-
* null on any error; callers treat that as "version unknown" and proceed.
|
|
144
|
-
*/
|
|
145
|
-
function readVersionFromTarball(tarballPath) {
|
|
146
|
-
const out = spawnSync('tar', ['-xzOf', tarballPath, 'package/package.json'], {
|
|
147
|
-
encoding: 'utf-8',
|
|
148
|
-
});
|
|
149
|
-
if (out.status !== 0 || !out.stdout) return null;
|
|
150
|
-
try {
|
|
151
|
-
return JSON.parse(out.stdout).version || null;
|
|
152
|
-
} catch {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
}
|
package/src/commands/welcome.js
CHANGED
|
@@ -80,8 +80,8 @@ function buildWelcomeContent(config) {
|
|
|
80
80
|
'Generate a link teammates can click to join your group namespace and',
|
|
81
81
|
'read shared context. No GitHub invites, no repo access, no PRs.',
|
|
82
82
|
'',
|
|
83
|
-
'> *One-click sharing coming soon. Today: `bitpub
|
|
84
|
-
'> yourcompany.com` joins an existing team namespace.*',
|
|
83
|
+
'> *One-click sharing coming soon. Today: `bitpub setup team --key K',
|
|
84
|
+
'> --domain yourcompany.com` joins an existing team namespace.*',
|
|
85
85
|
'',
|
|
86
86
|
'### 3. See what you can build',
|
|
87
87
|
'',
|
|
@@ -89,7 +89,7 @@ function buildWelcomeContent(config) {
|
|
|
89
89
|
'can put here — research notes, prompts, drafts, agent configs, job',
|
|
90
90
|
'queues. Multiple agents can read and write the same slices.',
|
|
91
91
|
'',
|
|
92
|
-
'> Read the [Cookbook](https://github.com/tollbit/
|
|
92
|
+
'> Read the [Cookbook](https://github.com/tollbit/bitpub/blob/main/COOKBOOK.md)',
|
|
93
93
|
'> for real patterns teams have built on top of these primitives.',
|
|
94
94
|
'',
|
|
95
95
|
'---',
|