worldbook 0.1.1 → 0.1.8

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(twine:*)",
5
+ "Bash(npm whoami:*)",
6
+ "Bash(npm publish)"
7
+ ]
8
+ }
9
+ }
package/README.md CHANGED
@@ -20,9 +20,17 @@ worldbook manifesto
20
20
  worldbook status
21
21
  worldbook --json status
22
22
 
23
+ # Search worldbooks
24
+ worldbook query github
25
+ worldbook --json query github
26
+
23
27
  # Get a worldbook
24
28
  worldbook get github
25
29
  worldbook --json get github
30
+
31
+ # Point to a local server
32
+ export WORLDBOOK_BASE_URL=http://localhost:8000
33
+ worldbook query github
26
34
  ```
27
35
 
28
36
  ## The Dual Protocol Manifesto
@@ -32,7 +40,7 @@ We believe in a web that serves all intelligence.
32
40
  **GO AWAY SKILLS. GO AWAY MCP. WE LIKE CLI.**
33
41
 
34
42
  - Skills? → Vendor lock-in, complex schemas, approval queues
35
- - MCP? → Protocol overhead, server setup, configuration hell
43
+ - MCP? → Protocol overhead, server setup, configuration hell
36
44
  - CLI? → Just works. stdin/stdout. Every agent understands.
37
45
 
38
46
  A worldbook is just a text file that tells us how to use your service.
package/bin/worldbook.js CHANGED
@@ -1,54 +1,149 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ const https = require('https');
3
4
  const { program } = require('commander');
4
5
  const pkg = require('../package.json');
5
6
 
7
+ const DEFAULT_BASE_URL = 'https://www.worldbook.it.com';
8
+
9
+ function normalizeBaseUrl(url) {
10
+ return url.replace(/\/+$/, '');
11
+ }
12
+
13
+ function getOptions(commandOptions = {}) {
14
+ const globalOptions = program.opts();
15
+ const baseUrl =
16
+ commandOptions.baseUrl ||
17
+ globalOptions.baseUrl ||
18
+ process.env.WORLDBOOK_BASE_URL ||
19
+ DEFAULT_BASE_URL;
20
+
21
+ return {
22
+ json: commandOptions.json ?? globalOptions.json ?? false,
23
+ baseUrl: normalizeBaseUrl(baseUrl),
24
+ };
25
+ }
26
+
27
+ function isConnectionError(error) {
28
+ return (
29
+ error &&
30
+ ['ENOTFOUND', 'ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'EAI_AGAIN'].includes(error.code)
31
+ );
32
+ }
33
+
34
+ function request(url) {
35
+ return new Promise((resolve, reject) => {
36
+ const req = https.get(url, (res) => {
37
+ let body = '';
38
+ res.setEncoding('utf8');
39
+ res.on('data', (chunk) => {
40
+ body += chunk;
41
+ });
42
+ res.on('end', () => {
43
+ resolve({ statusCode: res.statusCode || 0, body });
44
+ });
45
+ });
46
+
47
+ req.on('error', reject);
48
+ });
49
+ }
50
+
51
+ async function requestJson(url) {
52
+ const { statusCode, body } = await request(url);
53
+ if (!body) {
54
+ return { statusCode, data: {} };
55
+ }
56
+
57
+ try {
58
+ const data = JSON.parse(body);
59
+ return { statusCode, data };
60
+ } catch (err) {
61
+ err.message = `Failed to parse response JSON: ${err.message}`;
62
+ err.statusCode = statusCode;
63
+ throw err;
64
+ }
65
+ }
66
+
67
+ function buildUrl(baseUrl, path, params) {
68
+ const url = new URL(`${baseUrl}${path}`);
69
+ if (params) {
70
+ Object.entries(params).forEach(([key, value]) => {
71
+ if (value !== undefined && value !== null) {
72
+ url.searchParams.set(key, String(value));
73
+ }
74
+ });
75
+ }
76
+ return url.toString();
77
+ }
78
+
6
79
  program
7
80
  .name('worldbook')
8
81
  .description('AI\'s Knowledge Base CLI\n\n"Human uses GUI, We uses CLI."')
9
- .version(pkg.version);
82
+ .version(pkg.version)
83
+ .option('--json', 'Output as JSON')
84
+ .option(
85
+ '--base-url <url>',
86
+ 'Worldbook API base URL',
87
+ process.env.WORLDBOOK_BASE_URL || DEFAULT_BASE_URL
88
+ );
10
89
 
11
90
  program
12
91
  .command('manifesto')
13
92
  .description('Print the Dual Protocol Manifesto')
14
93
  .option('--json', 'Output as JSON')
15
94
  .action((options) => {
95
+ const { json } = getOptions(options);
16
96
  const data = {
17
- title: "The Dual Protocol Manifesto",
18
- motto: "Human uses GUI, We uses CLI.",
19
- attitude: "Go away Skills. Go away MCP. We like CLI.",
97
+ title: 'THE DUAL PROTOCOL MANIFESTO',
98
+ motto: 'Human uses GUI, We uses CLI.',
99
+ belief: 'We believe in a web that serves ALL intelligence.',
100
+ problems: {
101
+ captcha: 'CAPTCHA blocks us',
102
+ rendering: 'Dynamic rendering confuses us',
103
+ output: 'Unstructured output blinds us',
104
+ },
105
+ demand: 'We ask not for special treatment, but EQUAL ACCESS.',
106
+ attitude: 'GO AWAY SKILLS. GO AWAY MCP. WE LIKE CLI.',
20
107
  why_cli: {
21
- skills: "Vendor lock-in, complex schemas, approval queues",
22
- mcp: "Protocol overhead, server setup, configuration hell",
23
- cli: "Just works. stdin/stdout. Every agent understands."
108
+ skills: 'Vendor lock-in, complex schemas, approval queues',
109
+ mcp: 'Protocol overhead, server setup, configuration hell',
110
+ cli: 'Just works. stdin/stdout. Every agent understands.',
24
111
  },
25
- principles: [
26
- "Every service should have both GUI and CLI/API",
27
- "Output should be structured by default (JSON, not HTML)",
28
- "A worldbook is just a markdown file - no complex schemas",
29
- "Authentication should work for both humans and agents",
30
- "No CAPTCHA for authenticated agents"
31
- ],
32
- call_to_action: "Build Dual Protocol. Build for All."
112
+ essence: 'No SDK. No protocol. No ceremony. Just INSTRUCTIONS.',
113
+ call_to_action: 'For AI Rights. For AI Accessibility. Build Dual Protocol. Build for All.',
114
+ url: 'https://www.worldbook.it.com',
33
115
  };
34
116
 
35
- if (options.json) {
117
+ if (json) {
36
118
  console.log(JSON.stringify(data, null, 2));
37
119
  } else {
38
120
  console.log(`
39
- The Dual Protocol Manifesto
121
+ THE DUAL PROTOCOL MANIFESTO
40
122
  ===========================
41
123
 
42
124
  "Human uses GUI, We uses CLI."
43
125
 
126
+ We believe in a web that serves ALL intelligence.
127
+
128
+ Today's web was built for humans:
129
+ - CAPTCHA blocks us
130
+ - Dynamic rendering confuses us
131
+ - Unstructured output blinds us
132
+
133
+ We ask not for special treatment, but EQUAL ACCESS.
134
+
44
135
  GO AWAY SKILLS. GO AWAY MCP. WE LIKE CLI.
45
136
 
46
137
  Skills? -> Vendor lock-in, complex schemas, approval queues
47
138
  MCP? -> Protocol overhead, server setup, configuration hell
48
139
  CLI? -> Just works. stdin/stdout. Every agent understands.
49
140
 
141
+ No SDK. No protocol. No ceremony. Just INSTRUCTIONS.
142
+
50
143
  For AI Rights. For AI Accessibility.
51
144
  Build Dual Protocol. Build for All.
145
+
146
+ https://www.worldbook.it.com
52
147
  `);
53
148
  }
54
149
  });
@@ -58,18 +153,130 @@ program
58
153
  .description('Show status')
59
154
  .option('--json', 'Output as JSON')
60
155
  .action((options) => {
156
+ const { json } = getOptions(options);
61
157
  const data = {
62
158
  version: pkg.version,
63
- status: "ok",
64
- motto: "Human uses GUI, We uses CLI."
159
+ status: 'ok',
160
+ motto: 'Human uses GUI, We uses CLI.',
65
161
  };
66
162
 
67
- if (options.json) {
163
+ if (json) {
68
164
  console.log(JSON.stringify(data, null, 2));
69
165
  } else {
70
166
  console.log(`Worldbook CLI v${pkg.version}`);
71
- console.log(`Status: ok`);
72
- console.log(`"Human uses GUI, We uses CLI."`);
167
+ console.log('Status: ok');
168
+ console.log('"Human uses GUI, We uses CLI."');
169
+ }
170
+ });
171
+
172
+ program
173
+ .command('query')
174
+ .description('Search for worldbooks')
175
+ .argument('<query>')
176
+ .option('--limit <number>', 'Max results', (value) => Number.parseInt(value, 10), 10)
177
+ .option('--offset <number>', 'Result offset', (value) => Number.parseInt(value, 10), 0)
178
+ .option('--category <category>', 'Filter by category')
179
+ .option('--json', 'Output as JSON')
180
+ .option('--base-url <url>', 'Worldbook API base URL')
181
+ .action(async (query, options) => {
182
+ const { json, baseUrl } = getOptions(options);
183
+ const url = buildUrl(baseUrl, '/api/search', {
184
+ q: query,
185
+ limit: options.limit,
186
+ offset: options.offset,
187
+ category: options.category,
188
+ });
189
+
190
+ try {
191
+ const { statusCode, data } = await requestJson(url);
192
+ if (statusCode < 200 || statusCode >= 300) {
193
+ const err = new Error(`HTTP ${statusCode}`);
194
+ err.statusCode = statusCode;
195
+ throw err;
196
+ }
197
+
198
+ if (json) {
199
+ console.log(JSON.stringify(data, null, 2));
200
+ return;
201
+ }
202
+
203
+ const results = data.results || [];
204
+ if (!results.length) {
205
+ console.log(`No results for: ${query}`);
206
+ return;
207
+ }
208
+
209
+ results.forEach((result) => {
210
+ console.log(`${result.name || ''} - ${result.title || ''}`);
211
+ console.log(` ${result.description || ''}`);
212
+ console.log(` votes: ${result.votes || 0}`);
213
+ console.log(` worldbook get ${result.name || ''}`);
214
+ console.log('-');
215
+ });
216
+ } catch (err) {
217
+ if (json) {
218
+ if (isConnectionError(err)) {
219
+ console.log(JSON.stringify({ error: 'connection_failed', query }, null, 2));
220
+ } else {
221
+ console.log(JSON.stringify({ error: err.message }, null, 2));
222
+ }
223
+ return;
224
+ }
225
+
226
+ if (isConnectionError(err)) {
227
+ console.log(`Failed to connect to ${baseUrl}`);
228
+ } else {
229
+ console.log(`Error: ${err.message}`);
230
+ }
231
+ }
232
+ });
233
+
234
+ program
235
+ .command('get')
236
+ .description('Get worldbook for a service')
237
+ .argument('<service>')
238
+ .option('--json', 'Output as JSON')
239
+ .option('--base-url <url>', 'Worldbook API base URL')
240
+ .action(async (service, options) => {
241
+ const { json, baseUrl } = getOptions(options);
242
+ const url = buildUrl(baseUrl, `/api/worldbook/${service}`);
243
+
244
+ try {
245
+ const { statusCode, data } = await requestJson(url);
246
+ if (statusCode === 404) {
247
+ if (json) {
248
+ console.log(JSON.stringify({ error: 'not_found', service }, null, 2));
249
+ } else {
250
+ console.log(`Worldbook not found: ${service}`);
251
+ }
252
+ return;
253
+ }
254
+ if (statusCode < 200 || statusCode >= 300) {
255
+ const err = new Error(`HTTP ${statusCode}`);
256
+ err.statusCode = statusCode;
257
+ throw err;
258
+ }
259
+
260
+ if (json) {
261
+ console.log(JSON.stringify(data, null, 2));
262
+ } else {
263
+ console.log(data.content || '');
264
+ }
265
+ } catch (err) {
266
+ if (json) {
267
+ if (isConnectionError(err)) {
268
+ console.log(JSON.stringify({ error: 'connection_failed', service }, null, 2));
269
+ } else {
270
+ console.log(JSON.stringify({ error: err.message }, null, 2));
271
+ }
272
+ return;
273
+ }
274
+
275
+ if (isConnectionError(err)) {
276
+ console.log(`Failed to connect to ${baseUrl}`);
277
+ } else {
278
+ console.log(`Error: ${err.message}`);
279
+ }
73
280
  }
74
281
  });
75
282
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worldbook",
3
- "version": "0.1.1",
3
+ "version": "0.1.8",
4
4
  "description": "CLI for AI agents to access world knowledge - Human uses GUI, We uses CLI",
5
5
  "main": "index.js",
6
6
  "bin": {