kubun 0.7.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/README.md CHANGED
@@ -20,7 +20,7 @@ $ npm install -g kubun
20
20
  $ kubun COMMAND
21
21
  running command...
22
22
  $ kubun (--version)
23
- kubun/0.7.0 darwin-arm64 node-v24.14.0
23
+ kubun/0.8.0 darwin-arm64 node-v24.14.1
24
24
  $ kubun --help [COMMAND]
25
25
  USAGE
26
26
  $ kubun COMMAND
@@ -163,7 +163,7 @@ DESCRIPTION
163
163
  Display help for kubun.
164
164
  ```
165
165
 
166
- _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.37/src/commands/help.ts)_
166
+ _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/6.2.44/src/commands/help.ts)_
167
167
 
168
168
  ## `kubun mcp`
169
169
 
@@ -227,17 +227,19 @@ Start a local Kubun server
227
227
 
228
228
  ```
229
229
  USAGE
230
- $ kubun serve [-a <value>] [--db <value>] [-l trace|debug|info|warning|error|fatal] [-p <value>] [-k
231
- <value> | --id <value>]
230
+ $ kubun serve [-a <value>] [--autoAcceptPeers <value> --p2p] [--db <value>] [-l
231
+ trace|debug|info|warning|error|fatal] [-p <value>] [-k <value> | --id <value>]
232
232
 
233
233
  FLAGS
234
- -a, --allowedOrigin=<value> [default: *, env: KUBUN_ALLOWED_ORIGIN] allowed HTTP origin
235
- -k, --privateKey=<value> [env: KUBUN_PRIVATE_KEY] base64-encoded private key
236
- -l, --logLevel=<option> [default: warning] log level
237
- <options: trace|debug|info|warning|error|fatal>
238
- -p, --port=<value> port to listen on
239
- --db=<value> path to the local SQLite database
240
- --id=<value> server ID
234
+ -a, --allowedOrigin=<value> [default: *, env: KUBUN_ALLOWED_ORIGIN] allowed HTTP origin
235
+ -k, --privateKey=<value> [env: KUBUN_PRIVATE_KEY] base64-encoded private key
236
+ -l, --logLevel=<option> [default: warning] log level
237
+ <options: trace|debug|info|warning|error|fatal>
238
+ -p, --port=<value> port to listen on
239
+ --autoAcceptPeers=<value> comma-separated DIDs to auto-accept for peer join flows
240
+ --db=<value> path to the local SQLite database
241
+ --id=<value> server ID
242
+ --p2p enable P2P mode with sync and graph protocols over HTTP
241
243
 
242
244
  DESCRIPTION
243
245
  Start a local Kubun server
@@ -1,5 +1,5 @@
1
1
  import { createSchema } from '@kubun/graphql';
2
- import { GraphModel } from '@kubun/protocol';
2
+ import { clusterToRecord, GraphModel, } from '@kubun/protocol';
3
3
  import { Command, Flags } from '@oclif/core';
4
4
  import { printSchema } from 'graphql';
5
5
  import ora from 'ora';
@@ -21,7 +21,11 @@ export default class CreateGraphQLSchema extends Command {
21
21
  async run() {
22
22
  const spinner = ora('Loading clusters...').start();
23
23
  const { flags } = await this.parse(CreateGraphQLSchema);
24
- const clusters = await Promise.all(flags.cluster.map((path) => readJSON(path)));
24
+ const clusterModels = await Promise.all(flags.cluster.map((path) => readJSON(path)));
25
+ const clusters = {};
26
+ for (const cluster of clusterModels) {
27
+ Object.assign(clusters, clusterToRecord(cluster));
28
+ }
25
29
  const model = GraphModel.fromClusters({ clusters });
26
30
  const schema = printSchema(createSchema(model));
27
31
  if (flags.output == null) {
@@ -23,7 +23,7 @@ export default class MCP extends Command {
23
23
  };
24
24
  async run() {
25
25
  const { flags } = await this.parse(MCP);
26
- const config = createConfig({
26
+ const config = await createConfig({
27
27
  connect: flags.connect,
28
28
  database: flags.db,
29
29
  identity: flags.privateKey ? getSigner(flags.privateKey) : undefined,
@@ -1,5 +1,13 @@
1
- import { startServer } from '@kubun/http-server';
2
- import { getLogLevels } from '@logtape/logtape';
1
+ import { fromB64, toB64 } from '@enkaku/codec';
2
+ import { createFullIdentity, randomIdentity } from '@enkaku/token';
3
+ import { NodeSQLiteAdapter } from '@kubun/db-node-sqlite';
4
+ import { PostgresAdapter } from '@kubun/db-postgres';
5
+ import { KubunEngine } from '@kubun/engine';
6
+ import { getKubunLogger } from '@kubun/logger';
7
+ import { createHTTPPlugin } from '@kubun/plugin-http';
8
+ import { createP2PPlugin } from '@kubun/plugin-p2p';
9
+ import { createRPCPlugin } from '@kubun/plugin-rpc';
10
+ import { configureSync, getConsoleSink, getLogLevels } from '@logtape/logtape';
3
11
  import { Command, Flags } from '@oclif/core';
4
12
  import ora from 'ora';
5
13
  import { resolvePath } from '../fs.js';
@@ -12,6 +20,10 @@ export default class Serve extends Command {
12
20
  env: 'KUBUN_ALLOWED_ORIGIN',
13
21
  default: '*',
14
22
  }),
23
+ autoAcceptPeers: Flags.string({
24
+ description: 'comma-separated DIDs to auto-accept for peer join flows',
25
+ dependsOn: ['p2p'],
26
+ }),
15
27
  db: Flags.file({ description: 'path to the local SQLite database' }),
16
28
  id: Flags.string({ description: 'server ID' }),
17
29
  logLevel: Flags.string({
@@ -20,6 +32,7 @@ export default class Serve extends Command {
20
32
  options: getLogLevels(),
21
33
  default: 'warning',
22
34
  }),
35
+ p2p: Flags.boolean({ description: 'enable P2P mode with sync and graph protocols over HTTP' }),
23
36
  port: Flags.integer({ char: 'p', description: 'port to listen on' }),
24
37
  privateKey: Flags.string({
25
38
  char: 'k',
@@ -32,28 +45,79 @@ export default class Serve extends Command {
32
45
  const spinner = ora();
33
46
  const { flags } = await this.parse(Serve);
34
47
  spinner.start('Starting the server...');
35
- const options = {
36
- allowedOrigin: flags.allowedOrigin,
37
- logLevel: flags.logLevel,
38
- };
39
- if (flags.port != null) {
40
- options.port = flags.port;
41
- }
48
+ // Configure logging
49
+ configureSync({
50
+ reset: true,
51
+ sinks: { console: getConsoleSink() },
52
+ loggers: [
53
+ { category: ['logtape', 'meta'], lowestLevel: 'error', sinks: ['console'] },
54
+ {
55
+ category: ['kubun'],
56
+ lowestLevel: (flags.logLevel ?? 'warning'),
57
+ sinks: ['console'],
58
+ },
59
+ ],
60
+ });
61
+ // Resolve identity
62
+ const logger = getKubunLogger('cli');
63
+ let identity;
42
64
  if (flags.privateKey != null) {
43
- options.privateKey = flags.privateKey;
65
+ const privateKey = fromB64(flags.privateKey);
66
+ identity = { ...createFullIdentity(privateKey), privateKey };
67
+ }
68
+ else if (flags.id != null) {
69
+ if (flags.p2p) {
70
+ this.error('P2P mode requires a signing identity. Use --private-key instead of --id.');
71
+ }
72
+ identity = { id: flags.id };
73
+ }
74
+ else {
75
+ const generated = randomIdentity();
76
+ logger.warn('Generated random identity for server: {privateKey}, DID: {id}', {
77
+ id: generated.id,
78
+ privateKey: toB64(generated.privateKey),
79
+ });
80
+ identity = generated;
81
+ }
82
+ // Create DB adapter
83
+ const adapter = flags.db == null || flags.db === ':memory:'
84
+ ? new NodeSQLiteAdapter({ database: ':memory:' })
85
+ : flags.db.startsWith('postgres://') || flags.db.startsWith('postgresql://')
86
+ ? new PostgresAdapter({ url: flags.db })
87
+ : new NodeSQLiteAdapter({ database: resolvePath(flags.db) });
88
+ // Build plugin list
89
+ const autoAcceptPeers = flags.autoAcceptPeers
90
+ ? flags.autoAcceptPeers.split(',').map((s) => s.trim())
91
+ : undefined;
92
+ const rpcAccessControl = autoAcceptPeers != null ? { '*': [identity.id, ...autoAcceptPeers] } : undefined;
93
+ const plugins = [
94
+ createRPCPlugin(rpcAccessControl != null ? { accessControl: rpcAccessControl } : undefined),
95
+ createHTTPPlugin({ port: flags.port, allowedOrigin: flags.allowedOrigin }),
96
+ ];
97
+ if (flags.p2p) {
98
+ plugins.push(createP2PPlugin({ http: true, autoAcceptPeers }));
44
99
  }
45
- if (flags.id != null) {
46
- options.identity = { id: flags.id };
100
+ // Create engine with plugins
101
+ const engine = new KubunEngine({
102
+ db: { adapter },
103
+ identity,
104
+ plugins,
105
+ });
106
+ const http = await engine.getAPI('http');
107
+ await http.listening;
108
+ if (flags.p2p) {
109
+ const p2p = await engine.getAPI('p2p');
110
+ await p2p.syncReady;
47
111
  }
48
- if (flags.db != null) {
49
- options.db =
50
- flags.db.startsWith('postgres://') || flags.db.startsWith('postgresql://')
51
- ? flags.db
52
- : resolvePath(flags.db);
112
+ spinner.succeed(`HTTP server listening at ${http.getURL()}\n DID: ${identity.id}\n`);
113
+ if (flags.p2p && autoAcceptPeers) {
114
+ this.log(` Auto-accept peers: ${autoAcceptPeers.length} DID(s)`);
53
115
  }
54
- const server = await startServer(options);
55
- spinner.succeed(`HTTP server listening at ${server.url}`);
56
- // Keep process alive until server is closed (via SIGINT/SIGTERM handled by @kubun/http-server)
57
- await new Promise(() => { });
116
+ // Keep process alive until SIGINT/SIGTERM
117
+ await new Promise((resolve) => {
118
+ process.once('SIGINT', resolve);
119
+ process.once('SIGTERM', resolve);
120
+ });
121
+ await engine.dispose();
58
122
  }
59
123
  }
@@ -67,6 +67,16 @@
67
67
  "multiple": false,
68
68
  "type": "option"
69
69
  },
70
+ "autoAcceptPeers": {
71
+ "dependsOn": [
72
+ "p2p"
73
+ ],
74
+ "description": "comma-separated DIDs to auto-accept for peer join flows",
75
+ "name": "autoAcceptPeers",
76
+ "hasDynamicHelp": false,
77
+ "multiple": false,
78
+ "type": "option"
79
+ },
70
80
  "db": {
71
81
  "description": "path to the local SQLite database",
72
82
  "name": "db",
@@ -98,6 +108,12 @@
98
108
  ],
99
109
  "type": "option"
100
110
  },
111
+ "p2p": {
112
+ "description": "enable P2P mode with sync and graph protocols over HTTP",
113
+ "name": "p2p",
114
+ "allowNo": false,
115
+ "type": "boolean"
116
+ },
101
117
  "port": {
102
118
  "char": "p",
103
119
  "description": "port to listen on",
@@ -513,5 +529,5 @@
513
529
  ]
514
530
  }
515
531
  },
516
- "version": "0.7.0"
532
+ "version": "0.8.0"
517
533
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kubun",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "license": "see LICENSE.md",
5
5
  "description": "Kubun CLI",
6
6
  "keywords": [],
@@ -29,32 +29,38 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@enkaku/codec": "^0.14.0",
32
- "@enkaku/token": "0.14.0",
33
- "@logtape/logtape": "^2.0.4",
32
+ "@enkaku/token": "0.14.1",
33
+ "@logtape/logtape": "^2.0.5",
34
34
  "@mokei/context-server": "^0.6.0",
35
- "@oclif/core": "^4.8.3",
36
- "@oclif/plugin-help": "^6.2.37",
37
- "@oclif/plugin-plugins": "^5.4.57",
38
- "graphql": "^16.13.1",
35
+ "@oclif/core": "^4.10.3",
36
+ "@oclif/plugin-help": "^6.2.44",
37
+ "@oclif/plugin-plugins": "^5.4.60",
38
+ "graphql": "^16.13.2",
39
39
  "ora": "^9.3.0",
40
- "@kubun/client": "^0.7.0",
41
- "@kubun/http-client": "^0.7.0",
42
- "@kubun/graphql": "^0.7.0",
43
- "@kubun/http-server": "^0.7.0",
44
- "@kubun/protocol": "^0.7.0",
45
- "@kubun/mcp": "^0.7.0"
40
+ "@kubun/graphql": "^0.8.0",
41
+ "@kubun/client": "^0.8.0",
42
+ "@kubun/db-node-sqlite": "^0.8.0",
43
+ "@kubun/http-client": "^0.8.0",
44
+ "@kubun/engine": "^0.8.0",
45
+ "@kubun/db-postgres": "^0.8.0",
46
+ "@kubun/plugin-http": "^0.8.0",
47
+ "@kubun/plugin-p2p": "^0.8.0",
48
+ "@kubun/plugin-rpc": "^0.8.0",
49
+ "@kubun/protocol": "^0.8.0",
50
+ "@kubun/logger": "^0.8.0",
51
+ "@kubun/mcp": "^0.8.0"
46
52
  },
47
53
  "devDependencies": {
48
- "@oclif/test": "^4.1.16",
49
- "oclif": "^4.22.85",
54
+ "@oclif/test": "^4.1.17",
55
+ "oclif": "^4.22.96",
50
56
  "ts-node": "^10",
51
- "typescript": "^5.9.3"
57
+ "typescript": "^6.0.2"
52
58
  },
53
59
  "scripts": {
54
60
  "kubun": "./bin/dev.js",
55
61
  "build": "del dist && tsc -b",
56
62
  "posttest": "pnpm run lint",
57
- "test:types": "tsc --noEmit --skipLibCheck",
63
+ "test:types": "tsc --noEmit",
58
64
  "test:unit": "OCLIF_TEST_ROOT=. vitest run",
59
65
  "test": "pnpm run test:types && pnpm run test:unit",
60
66
  "version": "oclif readme && git add README.md"