kubun 0.10.0 → 0.12.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.
Files changed (55) hide show
  1. package/README.md +22 -234
  2. package/bin/dev.js +12 -4
  3. package/bin/run.js +11 -2
  4. package/lib/account.d.ts +2 -0
  5. package/lib/account.js +5 -0
  6. package/lib/client.d.ts +2 -0
  7. package/{dist → lib}/client.js +4 -1
  8. package/lib/commands/account.d.ts +2 -0
  9. package/lib/commands/account.js +24 -0
  10. package/lib/commands/graph.d.ts +2 -0
  11. package/lib/commands/graph.js +76 -0
  12. package/lib/commands/graphql.d.ts +2 -0
  13. package/lib/commands/graphql.js +33 -0
  14. package/lib/commands/hub.d.ts +2 -0
  15. package/lib/commands/hub.js +105 -0
  16. package/lib/commands/mcp.d.ts +2 -0
  17. package/lib/commands/mcp.js +21 -0
  18. package/lib/commands/model.d.ts +2 -0
  19. package/lib/commands/model.js +71 -0
  20. package/lib/commands/peer.d.ts +2 -0
  21. package/lib/commands/peer.js +275 -0
  22. package/lib/commands/serve.d.ts +2 -0
  23. package/lib/commands/serve.js +86 -0
  24. package/lib/engine.d.ts +74 -0
  25. package/lib/engine.js +154 -0
  26. package/lib/fs.d.ts +4 -0
  27. package/{dist → lib}/fs.js +6 -2
  28. package/lib/hub.d.ts +38 -0
  29. package/lib/hub.js +109 -0
  30. package/lib/identity.d.ts +19 -0
  31. package/lib/identity.js +31 -0
  32. package/lib/index.d.ts +3 -0
  33. package/lib/index.js +19 -0
  34. package/lib/options.d.ts +19 -0
  35. package/lib/options.js +47 -0
  36. package/lib/program.d.ts +2 -0
  37. package/lib/program.js +32 -0
  38. package/lib/ui.d.ts +5 -0
  39. package/lib/ui.js +29 -0
  40. package/package.json +47 -50
  41. package/bin/dev.cmd +0 -3
  42. package/bin/run.cmd +0 -3
  43. package/dist/account.js +0 -5
  44. package/dist/commands/account/generate.js +0 -10
  45. package/dist/commands/account/id.js +0 -18
  46. package/dist/commands/graph/deploy.js +0 -32
  47. package/dist/commands/graph/mutate.js +0 -32
  48. package/dist/commands/graph/query.js +0 -32
  49. package/dist/commands/graphql/schema.js +0 -41
  50. package/dist/commands/mcp.js +0 -33
  51. package/dist/commands/model/cluster.js +0 -35
  52. package/dist/commands/model/create.js +0 -69
  53. package/dist/commands/serve.js +0 -126
  54. package/dist/index.js +0 -1
  55. package/oclif.manifest.json +0 -533
@@ -4,10 +4,14 @@ export function resolvePath(value) {
4
4
  return isAbsolute(value) ? value : resolve(process.cwd(), value);
5
5
  }
6
6
  export async function readJSON(path) {
7
- return JSON.parse(await read(resolvePath(path), { encoding: 'utf8' }));
7
+ return JSON.parse(await read(resolvePath(path), {
8
+ encoding: 'utf8'
9
+ }));
8
10
  }
9
11
  export async function writeFile(path, value) {
10
- await write(resolvePath(path), value, { encoding: 'utf8' });
12
+ await write(resolvePath(path), value, {
13
+ encoding: 'utf8'
14
+ });
11
15
  }
12
16
  export async function writeJSON(path, value, format = false) {
13
17
  await writeFile(path, format ? JSON.stringify(value, null, 2) : JSON.stringify(value));
package/lib/hub.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { type HubAuthConfig, type Relay, type RelayObservers } from '@kubun/hub';
2
+ export type StartHubParams = {
3
+ /**
4
+ * Who may use the relay. Required: an unset `auth` would leave `createRelay`
5
+ * with no access rules at all, which refuses every DID without saying so.
6
+ */
7
+ auth: HubAuthConfig;
8
+ /** SQLite path, `:memory:`, or a postgres URL. Defaults to `:memory:`. */
9
+ db?: string;
10
+ /**
11
+ * Base64 private key for the hub's signing identity. Absent generates an
12
+ * ephemeral one — every client holding the old DID then has to rediscover it
13
+ * from `/info`, which is why `resolveIdentity` warns about it.
14
+ */
15
+ privateKey?: string;
16
+ allowedOrigin?: string;
17
+ /** Bind port. Absent probes for a free one, preferring {@link DEFAULT_HUB_PORT}. */
18
+ port?: number;
19
+ /** Relay diagnostics — auth refusals, stores, fetches. */
20
+ observers?: RelayObservers;
21
+ };
22
+ export type StartedHub = {
23
+ /** The relay endpoint a device binds a group to. */
24
+ url: string;
25
+ /** The hub's DID — the audience of every token it accepts, served at `/info`. */
26
+ did: string;
27
+ relay: Relay;
28
+ dispose: () => Promise<void>;
29
+ };
30
+ /**
31
+ * Serve a blind relay hub over HTTP.
32
+ *
33
+ * The hub holds no group state and is a member of nothing: it stores encrypted
34
+ * envelopes and serves topic subscriptions for groups it cannot read. Its only
35
+ * authentication is `auth`, checked against the issuer of each signed Enkaku
36
+ * envelope — there is nothing to check at the HTTP layer.
37
+ */
38
+ export declare function startHub(params: StartHubParams): Promise<StartedHub>;
package/lib/hub.js ADDED
@@ -0,0 +1,109 @@
1
+ import { serve } from '@hono/node-server';
2
+ import { NodeSQLiteAdapter } from '@kubun/db-node-sqlite';
3
+ import { PostgresAdapter } from '@kubun/db-postgres';
4
+ import { createRelay } from '@kubun/hub';
5
+ import { getKubunLogger } from '@kubun/logger';
6
+ import { getPort } from '@tejika/env';
7
+ import { resolvePath } from './fs.js';
8
+ import { resolveIdentity } from './identity.js';
9
+ /**
10
+ * Where the relay's Enkaku transport is mounted. The relay is path-agnostic —
11
+ * it is one transport behind a `fetch` — so this prefix is ours to pick, and it
12
+ * is the URL a device binds a group to.
13
+ */ const RELAY_PATH = '/relay';
14
+ /**
15
+ * The name `/info` advertises the relay mount under, mirroring how a device's
16
+ * HTTP surface advertises its own protocols. A device binds the mount path
17
+ * directly, so this is discovery for a reader that has only the origin.
18
+ */ const RELAY_PROTOCOL_NAME = 'relay';
19
+ /** Preferred port when none is given; a taken one yields another free port. */ const DEFAULT_HUB_PORT = 4321;
20
+ /**
21
+ * Serve a blind relay hub over HTTP.
22
+ *
23
+ * The hub holds no group state and is a member of nothing: it stores encrypted
24
+ * envelopes and serves topic subscriptions for groups it cannot read. Its only
25
+ * authentication is `auth`, checked against the issuer of each signed Enkaku
26
+ * envelope — there is nothing to check at the HTTP layer.
27
+ */ export async function startHub(params) {
28
+ const logger = getKubunLogger('cli');
29
+ // No `--id` counterpart here: the relay signs its own response tokens, so an
30
+ // identity that cannot sign is not a hub identity.
31
+ const identity = resolveIdentity({
32
+ privateKey: params.privateKey
33
+ }, logger);
34
+ const adapter = params.db == null || params.db === ':memory:' ? new NodeSQLiteAdapter({
35
+ database: ':memory:'
36
+ }) : params.db.startsWith('postgres://') || params.db.startsWith('postgresql://') ? new PostgresAdapter({
37
+ url: params.db
38
+ }) : new NodeSQLiteAdapter({
39
+ database: resolvePath(params.db)
40
+ });
41
+ const relay = await createRelay({
42
+ db: adapter,
43
+ identity,
44
+ auth: params.auth,
45
+ retention: {
46
+ olderThanSeconds: 30 * 24 * 60 * 60
47
+ },
48
+ ...params.allowedOrigin == null ? {} : {
49
+ allowedOrigin: params.allowedOrigin
50
+ },
51
+ ...params.observers == null ? {} : {
52
+ observers: params.observers
53
+ }
54
+ });
55
+ // OPTIONS is served alongside POST: the relay answers the CORS preflight
56
+ // itself, and a POST-only mount leaves a browser client unable to reach it.
57
+ const handler = async (request)=>{
58
+ const { pathname } = new URL(request.url);
59
+ if (pathname === RELAY_PATH || pathname.startsWith(`${RELAY_PATH}/`)) {
60
+ return request.method === 'POST' || request.method === 'OPTIONS' ? await relay.fetch(request) : new Response(null, {
61
+ status: 405
62
+ });
63
+ }
64
+ if (request.method === 'GET' && pathname === '/health') {
65
+ return Response.json({
66
+ status: 'ok'
67
+ });
68
+ }
69
+ // Discovery, in the shape a device already reads from another device's
70
+ // `/info`: the DID every token addressed to this hub must name, plus where
71
+ // the relay is mounted. A hub advertising a DID it cannot sign with is
72
+ // simply unreachable — its own server refuses the audience — so nothing
73
+ // here has to be verified by the reader.
74
+ if (request.method === 'GET' && pathname === '/info') {
75
+ return Response.json({
76
+ did: identity.id,
77
+ protocols: {
78
+ [RELAY_PROTOCOL_NAME]: RELAY_PATH
79
+ }
80
+ });
81
+ }
82
+ return new Response(null, {
83
+ status: 404
84
+ });
85
+ };
86
+ const port = params.port ?? await getPort('kubun', {
87
+ default: DEFAULT_HUB_PORT
88
+ });
89
+ const httpServer = await new Promise((resolve)=>{
90
+ const server = serve({
91
+ fetch: handler,
92
+ port
93
+ }, ()=>resolve(server));
94
+ });
95
+ return {
96
+ url: `http://localhost:${port}${RELAY_PATH}`,
97
+ did: identity.id,
98
+ relay,
99
+ dispose: async ()=>{
100
+ await new Promise((resolve, reject)=>{
101
+ httpServer.close((error)=>{
102
+ if (error == null) resolve();
103
+ else reject(error);
104
+ });
105
+ });
106
+ await relay.dispose();
107
+ }
108
+ };
109
+ }
@@ -0,0 +1,19 @@
1
+ import type { Identity, OwnIdentity } from '@kokuin/token';
2
+ type Logger = {
3
+ warn: (message: string, properties?: Record<string, unknown>) => void;
4
+ };
5
+ export type IdentityOptions = {
6
+ privateKey?: string;
7
+ id?: string;
8
+ p2p?: boolean;
9
+ };
10
+ /**
11
+ * Resolve the server signing identity from CLI options.
12
+ *
13
+ * The random-identity fallback logs only the DID — never the private key — and
14
+ * flags that the identity is ephemeral so it is not mistaken for a persistent
15
+ * one. Logging the key would leak the server's signing secret to console sinks
16
+ * and log aggregation.
17
+ */
18
+ export declare function resolveIdentity(opts: IdentityOptions, logger: Logger): Identity | OwnIdentity;
19
+ export {};
@@ -0,0 +1,31 @@
1
+ import { createFullIdentity, randomIdentity } from '@kokuin/token';
2
+ import { fromB64 } from '@sozai/codec';
3
+ /**
4
+ * Resolve the server signing identity from CLI options.
5
+ *
6
+ * The random-identity fallback logs only the DID — never the private key — and
7
+ * flags that the identity is ephemeral so it is not mistaken for a persistent
8
+ * one. Logging the key would leak the server's signing secret to console sinks
9
+ * and log aggregation.
10
+ */ export function resolveIdentity(opts, logger) {
11
+ if (opts.privateKey != null) {
12
+ const privateKey = fromB64(opts.privateKey);
13
+ return {
14
+ ...createFullIdentity(privateKey),
15
+ privateKey
16
+ };
17
+ }
18
+ if (opts.id != null) {
19
+ if (opts.p2p) {
20
+ throw new Error('P2P mode requires a signing identity. Use --private-key instead of --id.');
21
+ }
22
+ return {
23
+ id: opts.id
24
+ };
25
+ }
26
+ const generated = randomIdentity();
27
+ logger.warn('No identity provided; generated an ephemeral one (DID: {id}). It is discarded on exit — pass --private-key or --id to persist.', {
28
+ id: generated.id
29
+ });
30
+ return generated;
31
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { buildProgram } from './program.js';
2
+ export { buildProgram };
3
+ export declare function run(argv: Array<string>): Promise<void>;
package/lib/index.js ADDED
@@ -0,0 +1,19 @@
1
+ import { buildProgram } from './program.js';
2
+ export { buildProgram };
3
+ export async function run(argv) {
4
+ const program = buildProgram();
5
+ if (argv.slice(2).length === 0) {
6
+ program.outputHelp();
7
+ return;
8
+ }
9
+ try {
10
+ await program.parseAsync(argv);
11
+ } catch (error) {
12
+ const message = error instanceof Error ? error.message : String(error);
13
+ process.stderr.write(`✘ ${message}\n`);
14
+ if (process.env.KUBUN_DEBUG === '1' && error instanceof Error && error.stack != null) {
15
+ process.stderr.write(`${error.stack}\n`);
16
+ }
17
+ process.exitCode = 1;
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ import type { Command } from 'commander';
2
+ /** Add `-k, --private-key <key>`, bound to KUBUN_PRIVATE_KEY, optionally required. */
3
+ export declare function withPrivateKey(cmd: Command, opts?: {
4
+ required?: boolean;
5
+ }): Command;
6
+ /** Add `--url <url>` for the Kubun server URL. */
7
+ export declare function withURL(cmd: Command): Command;
8
+ /** Add `-l, --log-level <level>` with the `warning` default and the logtape level set. */
9
+ export declare function withLogLevel(cmd: Command): Command;
10
+ /** Add `-p, --port <port>`, default resolved lazily from @tejika/env at action time. */
11
+ export declare function withPort(cmd: Command): Command;
12
+ /**
13
+ * Add `-a, --allowed-origin <origin>`, bound to KUBUN_ALLOWED_ORIGIN.
14
+ *
15
+ * No default: an unset option denies cross-origin browser requests. Opening a
16
+ * surface is an explicit choice — pass `-a '*'` to reflect any origin, or a
17
+ * comma-separated list to allow only those.
18
+ */
19
+ export declare function withAllowedOrigin(cmd: Command): Command;
package/lib/options.js ADDED
@@ -0,0 +1,47 @@
1
+ import { getLogLevels } from '@logtape/logtape';
2
+ import { getAppEnvVar, getPort } from '@tejika/env';
3
+ /** Add `-k, --private-key <key>`, bound to KUBUN_PRIVATE_KEY, optionally required. */ export function withPrivateKey(cmd, opts = {}) {
4
+ cmd.option('-k, --private-key <key>', 'base64-encoded private key (env: KUBUN_PRIVATE_KEY)');
5
+ cmd.hook('preAction', (_thisCmd, actionCmd)=>{
6
+ if (actionCmd.opts().privateKey == null && process.env.KUBUN_PRIVATE_KEY != null) {
7
+ actionCmd.setOptionValue('privateKey', process.env.KUBUN_PRIVATE_KEY);
8
+ }
9
+ if (opts.required && actionCmd.opts().privateKey == null) {
10
+ throw new Error('--private-key is required (or set KUBUN_PRIVATE_KEY)');
11
+ }
12
+ });
13
+ return cmd;
14
+ }
15
+ /** Add `--url <url>` for the Kubun server URL. */ export function withURL(cmd) {
16
+ return cmd.option('--url <url>', 'Kubun server URL');
17
+ }
18
+ /** Add `-l, --log-level <level>` with the `warning` default and the logtape level set. */ export function withLogLevel(cmd) {
19
+ return cmd.option('-l, --log-level <level>', `log level (${getLogLevels().join(', ')})`, 'warning');
20
+ }
21
+ /** Add `-p, --port <port>`, default resolved lazily from @tejika/env at action time. */ export function withPort(cmd) {
22
+ cmd.option('-p, --port <port>', 'port to listen on');
23
+ cmd.hook('preAction', async (_thisCmd, actionCmd)=>{
24
+ if (actionCmd.opts().port == null) {
25
+ actionCmd.setOptionValue('port', await getPort('kubun'));
26
+ }
27
+ });
28
+ return cmd;
29
+ }
30
+ /**
31
+ * Add `-a, --allowed-origin <origin>`, bound to KUBUN_ALLOWED_ORIGIN.
32
+ *
33
+ * No default: an unset option denies cross-origin browser requests. Opening a
34
+ * surface is an explicit choice — pass `-a '*'` to reflect any origin, or a
35
+ * comma-separated list to allow only those.
36
+ */ export function withAllowedOrigin(cmd) {
37
+ cmd.option('-a, --allowed-origin <origin>', 'allowed HTTP origin(s), comma-separated (env: KUBUN_ALLOWED_ORIGIN); unset denies cross-origin browser requests');
38
+ cmd.hook('preAction', (_thisCmd, actionCmd)=>{
39
+ if (actionCmd.opts().allowedOrigin == null) {
40
+ const envOrigin = getAppEnvVar('kubun', 'ALLOWED_ORIGIN');
41
+ if (envOrigin != null) {
42
+ actionCmd.setOptionValue('allowedOrigin', envOrigin);
43
+ }
44
+ }
45
+ });
46
+ return cmd;
47
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function buildProgram(): Command;
package/lib/program.js ADDED
@@ -0,0 +1,32 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { buildProgram as tejikaBuildProgram } from '@tejika/cli';
5
+ import { createAccountCommand } from './commands/account.js';
6
+ import { createGraphCommand } from './commands/graph.js';
7
+ import { createGraphQLCommand } from './commands/graphql.js';
8
+ import { createHubCommand } from './commands/hub.js';
9
+ import { createMCPCommand } from './commands/mcp.js';
10
+ import { createModelCommand } from './commands/model.js';
11
+ import { createPeerCommand } from './commands/peer.js';
12
+ import { createServeCommand } from './commands/serve.js';
13
+ const pkgPath = resolve(dirname(fileURLToPath(import.meta.url)), '../package.json');
14
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
15
+ export function buildProgram() {
16
+ const program = tejikaBuildProgram({
17
+ name: 'kubun',
18
+ version: pkg.version,
19
+ commands: [
20
+ createAccountCommand(),
21
+ createGraphCommand(),
22
+ createGraphQLCommand(),
23
+ createHubCommand(),
24
+ createModelCommand(),
25
+ createMCPCommand(),
26
+ createPeerCommand(),
27
+ createServeCommand()
28
+ ]
29
+ });
30
+ program.description(pkg.description);
31
+ return program;
32
+ }
package/lib/ui.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { type SystemNoticeVariant } from '@tejika/ui';
2
+ /** Render a one-shot status notice to stderr, then unmount. */
3
+ export declare function renderNotice(variant: SystemNoticeVariant, text: string): void;
4
+ /** Show a live spinner on stderr for the duration of `fn`, then unmount. */
5
+ export declare function withSpinner<T>(label: string, fn: () => Promise<T>): Promise<T>;
package/lib/ui.js ADDED
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Spinner, SystemNotice } from '@tejika/ui';
3
+ import { render } from 'ink';
4
+ const renderToStderr = {
5
+ stdout: process.stderr
6
+ };
7
+ /** Render a one-shot status notice to stderr, then unmount. */ export function renderNotice(variant, text) {
8
+ const { unmount } = render(/*#__PURE__*/ _jsx(SystemNotice, {
9
+ variant: variant,
10
+ text: text
11
+ }), {
12
+ ...renderToStderr,
13
+ // Nothing to protect from interleaved logs: this mounts and unmounts in one
14
+ // turn. Patching the global console for that costs a process-wide side
15
+ // effect, and throws outright wherever `console` is already a stand-in.
16
+ patchConsole: false
17
+ });
18
+ unmount();
19
+ }
20
+ /** Show a live spinner on stderr for the duration of `fn`, then unmount. */ export async function withSpinner(label, fn) {
21
+ const app = render(/*#__PURE__*/ _jsx(Spinner, {
22
+ label: label
23
+ }), renderToStderr);
24
+ try {
25
+ return await fn();
26
+ } finally{
27
+ app.unmount();
28
+ }
29
+ }
package/package.json CHANGED
@@ -1,68 +1,65 @@
1
1
  {
2
2
  "name": "kubun",
3
- "version": "0.10.0",
4
- "license": "see LICENSE.md",
3
+ "version": "0.12.0",
5
4
  "description": "Kubun CLI",
6
5
  "keywords": [],
6
+ "license": "see LICENSE.md",
7
7
  "type": "module",
8
- "engines": {
9
- "node": ">=22.0.0"
8
+ "main": "lib/index.js",
9
+ "types": "lib/index.d.ts",
10
+ "bin": {
11
+ "kubun": "./bin/run.js"
10
12
  },
11
- "main": "dist/index.js",
12
- "types": "dist/index.d.ts",
13
13
  "files": [
14
14
  "/bin",
15
- "/dist",
16
- "/oclif.manifest.json"
15
+ "/lib"
17
16
  ],
18
- "bin": {
19
- "kubun": "./bin/run.js"
20
- },
21
- "oclif": {
22
- "bin": "kubun",
23
- "dirname": "kubun",
24
- "commands": "./dist/commands",
25
- "plugins": [
26
- "@oclif/plugin-help"
27
- ],
28
- "topicSeparator": " "
29
- },
30
17
  "dependencies": {
31
- "@enkaku/codec": "^0.16.0",
32
- "@enkaku/token": "^0.16.0",
33
- "@logtape/logtape": "^2.1.1",
34
- "@mokei/context-server": "^0.7.0",
35
- "@oclif/core": "^4.11.4",
36
- "@oclif/plugin-help": "^6.2.49",
37
- "@oclif/plugin-plugins": "^5.4.69",
38
- "graphql": "^16.13.2",
39
- "ora": "^9.4.0",
40
- "@kubun/client": "^0.10.0",
41
- "@kubun/db-node-sqlite": "^0.10.0",
42
- "@kubun/db-postgres": "^0.10.0",
43
- "@kubun/graphql": "^0.10.0",
44
- "@kubun/engine": "^0.10.0",
45
- "@kubun/plugin-http": "^0.10.0",
46
- "@kubun/http-client": "^0.10.0",
47
- "@kubun/plugin-p2p": "^0.10.0",
48
- "@kubun/mcp": "^0.10.0",
49
- "@kubun/logger": "^0.10.0",
50
- "@kubun/plugin-rpc": "^0.10.0",
51
- "@kubun/protocol": "^0.10.0"
18
+ "@hono/node-server": "^2.1.0",
19
+ "@kokuin/token": "^0.3.0",
20
+ "@logtape/logtape": "^2.3.0",
21
+ "@mokei/context-server": "^0.11.0",
22
+ "@sozai/codec": "^0.4.0",
23
+ "@tejika/cli": "^0.4.0",
24
+ "@tejika/env": "^0.4.0",
25
+ "@tejika/ui": "^0.4.0",
26
+ "commander": "^15.0.0",
27
+ "graphql": "^16.14.2",
28
+ "ink": "^7.1.1",
29
+ "react": "19.2.3",
30
+ "@kubun/db": "^0.12.0",
31
+ "@kubun/client": "^0.12.0",
32
+ "@kubun/db-node-sqlite": "^0.12.0",
33
+ "@kubun/graphql": "^0.12.0",
34
+ "@kubun/http-client": "^0.12.0",
35
+ "@kubun/logger": "^0.12.0",
36
+ "@kubun/db-postgres": "^0.12.0",
37
+ "@kubun/mcp": "^0.12.0",
38
+ "@kubun/hub": "^0.12.0",
39
+ "@kubun/engine": "^0.12.0",
40
+ "@kubun/plugin-p2p": "^0.12.0",
41
+ "@kubun/plugin-http": "^0.12.0",
42
+ "@kubun/protocol": "^0.12.0",
43
+ "@kubun/plugin-rpc": "^0.12.0"
52
44
  },
53
45
  "devDependencies": {
54
- "@oclif/test": "^4.1.17",
55
- "oclif": "^4.23.8",
56
- "ts-node": "^10",
57
- "typescript": "^6.0.3"
46
+ "ink-testing-library": "^4.0.0",
47
+ "@kubun/store-graph": "^0.12.0",
48
+ "@kubun/id": "^0.12.0",
49
+ "@kubun/store-p2p": "^0.12.0"
50
+ },
51
+ "engines": {
52
+ "node": ">=22.0.0"
58
53
  },
59
54
  "scripts": {
55
+ "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
56
+ "build:clean": "del lib",
57
+ "build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
58
+ "build:types": "tsc --emitDeclarationOnly --skipLibCheck",
59
+ "build:types:ci": "tsc --emitDeclarationOnly --skipLibCheck --declarationMap false",
60
60
  "kubun": "./bin/dev.js",
61
- "build": "del dist && tsc -b",
62
- "posttest": "pnpm run lint",
63
- "test:types": "tsc --noEmit",
64
- "test:unit": "OCLIF_TEST_ROOT=. vitest run",
65
61
  "test": "pnpm run test:types && pnpm run test:unit",
66
- "version": "oclif readme && git add README.md"
62
+ "test:types": "tsc --noEmit --skipLibCheck -p tsconfig.test.json",
63
+ "test:unit": "vitest run"
67
64
  }
68
65
  }
package/bin/dev.cmd DELETED
@@ -1,3 +0,0 @@
1
- @echo off
2
-
3
- node --loader ts-node/esm --no-warnings=ExperimentalWarning "%~dp0\dev" %*
package/bin/run.cmd DELETED
@@ -1,3 +0,0 @@
1
- @echo off
2
-
3
- node "%~dp0\run" %*
package/dist/account.js DELETED
@@ -1,5 +0,0 @@
1
- import { fromB64 } from '@enkaku/codec';
2
- import { createSigningIdentity } from '@enkaku/token';
3
- export function getSigner(key) {
4
- return createSigningIdentity(fromB64(key));
5
- }
@@ -1,10 +0,0 @@
1
- import { toB64 } from '@enkaku/codec';
2
- import { randomPrivateKey } from '@enkaku/token';
3
- import { Command } from '@oclif/core';
4
- export default class GenerateAccount extends Command {
5
- static description = 'Generate a random private key to use as account signer';
6
- async run() {
7
- const key = await randomPrivateKey();
8
- this.log(toB64(key));
9
- }
10
- }
@@ -1,18 +0,0 @@
1
- import { Command, Flags } from '@oclif/core';
2
- import { getSigner } from '../../account.js';
3
- export default class AccountID extends Command {
4
- static description = 'Display the DID of an account given its private key';
5
- static flags = {
6
- privateKey: Flags.string({
7
- description: 'base64-encoded private key',
8
- char: 'k',
9
- env: 'KUBUN_PRIVATE_KEY',
10
- required: true,
11
- }),
12
- };
13
- async run() {
14
- const { flags } = await this.parse(AccountID);
15
- const signer = getSigner(flags.privateKey);
16
- this.log(signer.id);
17
- }
18
- }
@@ -1,32 +0,0 @@
1
- import { Command, Flags } from '@oclif/core';
2
- import ora from 'ora';
3
- import { createClient } from '../../client.js';
4
- import { readJSON } from '../../fs.js';
5
- export default class DeployGraph extends Command {
6
- static description = 'Deploy a documents model graph';
7
- static flags = {
8
- cluster: Flags.string({
9
- description: 'path of the JSON file of the cluster',
10
- char: 'c',
11
- required: true,
12
- multiple: true,
13
- }),
14
- id: Flags.string({ description: 'graph ID' }),
15
- name: Flags.string({ description: 'graph name', char: 'n' }),
16
- privateKey: Flags.string({
17
- description: 'base64-encoded private key',
18
- char: 'k',
19
- env: 'KUBUN_PRIVATE_KEY',
20
- required: true,
21
- }),
22
- url: Flags.string({ description: 'Kubun server URL' }),
23
- };
24
- async run() {
25
- const spinner = ora('Deploying graph clusters...').start();
26
- const { flags } = await this.parse(DeployGraph);
27
- const client = createClient(flags.privateKey, flags.url);
28
- const clusters = await Promise.all(flags.cluster.map((path) => readJSON(path)));
29
- const graph = await client.deployGraph({ clusters, id: flags.id, name: flags.name });
30
- spinner.succeed(`Deployed graph with ID: ${graph.id}`);
31
- }
32
- }
@@ -1,32 +0,0 @@
1
- import { Args, Command, Flags } from '@oclif/core';
2
- import ora from 'ora';
3
- import { createClient } from '../../client.js';
4
- export default class MutateGraph extends Command {
5
- static description = 'Execute a GraphQL mutation on a graph';
6
- static args = {
7
- id: Args.string({ description: 'graph ID', required: true }),
8
- text: Args.string({ description: 'mutation text', required: true }),
9
- };
10
- static flags = {
11
- privateKey: Flags.string({
12
- description: 'base64-encoded private key',
13
- char: 'k',
14
- env: 'KUBUN_PRIVATE_KEY',
15
- required: true,
16
- }),
17
- url: Flags.string({ description: 'Kubun server URL' }),
18
- variables: Flags.string({ description: 'query variables as JSON string' }),
19
- };
20
- async run() {
21
- const spinner = ora('Executing graph mutation...').start();
22
- const { args, flags } = await this.parse(MutateGraph);
23
- const client = createClient(flags.privateKey, flags.url);
24
- const result = await client.mutateGraph({
25
- id: args.id,
26
- text: args.text,
27
- variables: flags.variables ? JSON.parse(flags.variables) : {},
28
- });
29
- spinner.succeed('Graph mutation executed');
30
- this.log(JSON.stringify(result, null, 2));
31
- }
32
- }
@@ -1,32 +0,0 @@
1
- import { Args, Command, Flags } from '@oclif/core';
2
- import ora from 'ora';
3
- import { createClient } from '../../client.js';
4
- export default class QueryGraph extends Command {
5
- static description = 'Execute a GraphQL query on a graph';
6
- static args = {
7
- id: Args.string({ description: 'graph ID', required: true }),
8
- text: Args.string({ description: 'query text', required: true }),
9
- };
10
- static flags = {
11
- privateKey: Flags.string({
12
- description: 'base64-encoded private key',
13
- char: 'k',
14
- env: 'KUBUN_PRIVATE_KEY',
15
- required: true,
16
- }),
17
- url: Flags.string({ description: 'Kubun server URL' }),
18
- variables: Flags.string({ description: 'query variables as JSON string' }),
19
- };
20
- async run() {
21
- const spinner = ora('Querying graph...').start();
22
- const { args, flags } = await this.parse(QueryGraph);
23
- const client = createClient(flags.privateKey, flags.url);
24
- const result = await client.queryGraph({
25
- id: args.id,
26
- text: args.text,
27
- variables: flags.variables ? JSON.parse(flags.variables) : {},
28
- });
29
- spinner.succeed('Graph query executed');
30
- this.log(JSON.stringify(result, null, 2));
31
- }
32
- }