@qnote/q-ai-note 1.0.5 → 1.0.6
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 +44 -0
- package/dist/cli.js +68 -18
- package/dist/cli.js.map +1 -1
- package/dist/server/index.d.ts +7 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +62 -4
- package/dist/server/index.js.map +1 -1
- package/dist/web/app.js +176 -40
- package/dist/web/index.html +4 -1
- package/dist/web/styles.css +67 -7
- package/dist/web/vueRenderers.js +64 -52
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
AI-assisted personal work sandbox and diary system.
|
|
4
4
|
|
|
5
|
+
## Documentation
|
|
6
|
+
|
|
7
|
+
Project specs and development guides are organized under `docs/`:
|
|
8
|
+
|
|
9
|
+
- `docs/spec/functional-spec.md`
|
|
10
|
+
- `docs/spec/design-spec.md`
|
|
11
|
+
- `docs/spec/ui-spec.md`
|
|
12
|
+
- `docs/process/development-guide.md`
|
|
13
|
+
|
|
5
14
|
## Install
|
|
6
15
|
|
|
7
16
|
Use without global install:
|
|
@@ -16,6 +25,7 @@ Install globally:
|
|
|
16
25
|
npm i -g @qnote/q-ai-note
|
|
17
26
|
q-ai-note
|
|
18
27
|
q-ai-note-server
|
|
28
|
+
q-ai-note-my-server
|
|
19
29
|
```
|
|
20
30
|
|
|
21
31
|
## Usage
|
|
@@ -23,16 +33,24 @@ q-ai-note-server
|
|
|
23
33
|
```bash
|
|
24
34
|
q-ai-note --port 3200
|
|
25
35
|
q-ai-note .
|
|
36
|
+
q-ai-note --readonly
|
|
26
37
|
```
|
|
27
38
|
|
|
28
39
|
Then open `http://localhost:3200`.
|
|
29
40
|
|
|
41
|
+
### Command differences
|
|
42
|
+
|
|
43
|
+
- `q-ai-note`: local-first, defaults `127.0.0.1:3000`
|
|
44
|
+
- `q-ai-note-server`: public server, defaults `0.0.0.0:8614`
|
|
45
|
+
- `q-ai-note-my-server`: mixed policy server, defaults `0.0.0.0:3000`
|
|
46
|
+
|
|
30
47
|
### Public server mode
|
|
31
48
|
|
|
32
49
|
If you need external access, use:
|
|
33
50
|
|
|
34
51
|
```bash
|
|
35
52
|
q-ai-note-server
|
|
53
|
+
q-ai-note-server --readonly
|
|
36
54
|
```
|
|
37
55
|
|
|
38
56
|
Defaults:
|
|
@@ -41,6 +59,32 @@ Defaults:
|
|
|
41
59
|
|
|
42
60
|
Then open `http://<server-ip>:8614`.
|
|
43
61
|
|
|
62
|
+
### My server mode
|
|
63
|
+
|
|
64
|
+
If you want one server with mixed access policy, use:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
q-ai-note-my-server
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Defaults:
|
|
71
|
+
- host: `0.0.0.0`
|
|
72
|
+
- port: `3000`
|
|
73
|
+
|
|
74
|
+
Policy:
|
|
75
|
+
- access from `localhost` / `127.0.0.1` / local machine IP -> full features (readwrite)
|
|
76
|
+
- access from other IPs -> readonly (both UI and write APIs)
|
|
77
|
+
|
|
78
|
+
### Readonly mode
|
|
79
|
+
|
|
80
|
+
Use `--readonly` (or env `Q_AI_NOTE_READONLY=true`) to start in readonly mode:
|
|
81
|
+
|
|
82
|
+
- hide chat UI and chat buttons
|
|
83
|
+
- hide settings page
|
|
84
|
+
- hide add/edit/delete operations
|
|
85
|
+
- reject all write API requests (`403`)
|
|
86
|
+
- reject settings API (`/api/settings`) for readonly requests (`403`)
|
|
87
|
+
|
|
44
88
|
## Data and config paths
|
|
45
89
|
|
|
46
90
|
- Config: `~/.q-ai-note/config.json`
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { basename } from 'path';
|
|
3
4
|
import { startServer } from './server/index.js';
|
|
4
|
-
function
|
|
5
|
+
function resolveCliDefaults(argv0) {
|
|
6
|
+
const commandName = basename(argv0 || 'q-ai-note');
|
|
7
|
+
const isServerMode = commandName === 'q-ai-note-server';
|
|
8
|
+
const isMyServerMode = commandName === 'q-ai-note-my-server';
|
|
9
|
+
return {
|
|
10
|
+
commandName,
|
|
11
|
+
defaultPort: isMyServerMode ? 3000 : isServerMode ? 8614 : 3000,
|
|
12
|
+
defaultHost: (isServerMode || isMyServerMode) ? '0.0.0.0' : '127.0.0.1',
|
|
13
|
+
localWriteOnly: isMyServerMode,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function parseArgs(argv, defaults) {
|
|
5
17
|
const options = {
|
|
6
|
-
port: Number(process.env.PORT ||
|
|
7
|
-
host: String(process.env.HOST ||
|
|
18
|
+
port: Number(process.env.PORT || defaults.defaultPort),
|
|
19
|
+
host: String(process.env.HOST || defaults.defaultHost),
|
|
20
|
+
readonly: String(process.env.Q_AI_NOTE_READONLY || '').toLowerCase() === 'true',
|
|
8
21
|
help: false,
|
|
9
22
|
dataDir: undefined,
|
|
10
23
|
};
|
|
@@ -33,46 +46,83 @@ function parseArgs(argv) {
|
|
|
33
46
|
}
|
|
34
47
|
continue;
|
|
35
48
|
}
|
|
49
|
+
if (arg === '--readonly') {
|
|
50
|
+
options.readonly = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
36
53
|
if (!arg.startsWith('-') && !options.dataDir) {
|
|
37
54
|
options.dataDir = path.resolve(arg);
|
|
38
55
|
}
|
|
39
56
|
}
|
|
40
57
|
return options;
|
|
41
58
|
}
|
|
42
|
-
function printHelp() {
|
|
59
|
+
function printHelp(defaults) {
|
|
60
|
+
const isServerMode = defaults.commandName === 'q-ai-note-server';
|
|
61
|
+
const isMyServerMode = defaults.commandName === 'q-ai-note-my-server';
|
|
62
|
+
const defaultPortText = isMyServerMode ? '3000 or PORT env' : isServerMode ? '8614' : '3000 or PORT env';
|
|
63
|
+
const examples = isMyServerMode
|
|
64
|
+
? [
|
|
65
|
+
' q-ai-note-my-server',
|
|
66
|
+
' q-ai-note-my-server --port 3000',
|
|
67
|
+
' q-ai-note-my-server --host 0.0.0.0 --port 3000',
|
|
68
|
+
' q-ai-note-my-server ./my-data --port 3000',
|
|
69
|
+
]
|
|
70
|
+
: isServerMode
|
|
71
|
+
? [
|
|
72
|
+
' q-ai-note-server',
|
|
73
|
+
' q-ai-note-server --port 8614',
|
|
74
|
+
' q-ai-note-server --host 0.0.0.0 --port 8614',
|
|
75
|
+
' q-ai-note-server ./my-data --port 8614',
|
|
76
|
+
]
|
|
77
|
+
: [
|
|
78
|
+
' npx @qnote/q-ai-note',
|
|
79
|
+
' q-ai-note .',
|
|
80
|
+
' q-ai-note --port 3200',
|
|
81
|
+
' q-ai-note --host 127.0.0.1 --port 3200',
|
|
82
|
+
' q-ai-note ./my-data --port 3200',
|
|
83
|
+
' PORT=3200 q-ai-note',
|
|
84
|
+
];
|
|
43
85
|
console.log(`
|
|
44
|
-
|
|
86
|
+
${defaults.commandName}
|
|
45
87
|
|
|
46
88
|
Usage:
|
|
47
|
-
|
|
89
|
+
${defaults.commandName} [data-dir] [--port <port>] [--host <host>] [--readonly]
|
|
48
90
|
|
|
49
91
|
Options:
|
|
50
92
|
data-dir Directory for JSON data files (optional)
|
|
51
|
-
-p, --port Set server port (default:
|
|
52
|
-
--host Set listen host (default:
|
|
93
|
+
-p, --port Set server port (default: ${defaultPortText})
|
|
94
|
+
--host Set listen host (default: ${defaults.defaultHost})
|
|
95
|
+
--readonly Start in readonly mode
|
|
53
96
|
-h, --help Show this help message
|
|
54
97
|
|
|
55
98
|
Examples:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
q-ai-note --host 127.0.0.1 --port 3200
|
|
60
|
-
q-ai-note ./my-data --port 3200
|
|
61
|
-
PORT=3200 q-ai-note
|
|
99
|
+
${examples.join('\n')}
|
|
100
|
+
|
|
101
|
+
${isMyServerMode ? 'Behavior:\n local access (localhost/127.0.0.1/local machine IP) => readwrite\n non-local access => readonly' : ''}
|
|
62
102
|
`);
|
|
63
103
|
}
|
|
64
104
|
function bootstrap() {
|
|
65
|
-
const
|
|
105
|
+
const defaults = resolveCliDefaults(process.argv[1]);
|
|
106
|
+
const options = parseArgs(process.argv.slice(2), defaults);
|
|
66
107
|
if (options.help) {
|
|
67
|
-
printHelp();
|
|
108
|
+
printHelp(defaults);
|
|
68
109
|
return;
|
|
69
110
|
}
|
|
70
111
|
if (options.dataDir) {
|
|
71
112
|
process.env.Q_AI_NOTE_DATA_DIR = options.dataDir;
|
|
72
113
|
}
|
|
73
|
-
startServer(options.port, options.host
|
|
74
|
-
|
|
114
|
+
startServer(options.port, options.host, {
|
|
115
|
+
readonly: options.readonly,
|
|
116
|
+
localWriteOnly: defaults.localWriteOnly && !options.readonly,
|
|
117
|
+
});
|
|
118
|
+
console.log(`${defaults.commandName} is ready at http://localhost:${options.port}`);
|
|
75
119
|
console.log(`listen: ${options.host}:${options.port}`);
|
|
120
|
+
const modeText = options.readonly
|
|
121
|
+
? 'readonly'
|
|
122
|
+
: defaults.localWriteOnly
|
|
123
|
+
? 'local-write-only'
|
|
124
|
+
: 'readwrite';
|
|
125
|
+
console.log(`mode: ${modeText}`);
|
|
76
126
|
if (options.dataDir) {
|
|
77
127
|
console.log(`data dir: ${options.dataDir}`);
|
|
78
128
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { Server } from 'node:http';
|
|
2
|
-
export
|
|
3
|
-
|
|
2
|
+
export interface AccessPolicy {
|
|
3
|
+
readonly?: boolean;
|
|
4
|
+
localWriteOnly?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function isReadonlyForIp(ip: string, policyInput?: boolean | AccessPolicy, localIpSet?: Set<string>): boolean;
|
|
7
|
+
export declare function createApp(inputPolicy?: boolean | AccessPolicy): import("express-serve-static-core").Express;
|
|
8
|
+
export declare function startServer(port?: number, host?: string, policyInput?: boolean | AccessPolicy): Server;
|
|
4
9
|
export declare function stopServer(): Promise<void>;
|
|
5
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAqBxC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAoBD,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,EACV,WAAW,GAAE,OAAO,GAAG,YAAoB,EAC3C,UAAU,GAAE,GAAG,CAAC,MAAM,CAAqB,GAC1C,OAAO,CAKT;AAYD,wBAAgB,SAAS,CAAC,WAAW,GAAE,OAAO,GAAG,YAAuB,+CA4CvE;AAED,wBAAgB,WAAW,CACzB,IAAI,SAAO,EACX,IAAI,SAAO,EACX,WAAW,GAAE,OAAO,GAAG,YAAuE,GAC7F,MAAM,CAmBR;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAkBhD"}
|
package/dist/server/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
3
4
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
4
5
|
import { initDb, closeDb } from './db.js';
|
|
5
6
|
import sandboxRouter from './api/sandbox.js';
|
|
@@ -11,13 +12,68 @@ import nodeEntitiesRouter from './api/nodeEntities.js';
|
|
|
11
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
13
|
const PORT = Number(process.env.PORT || 3000);
|
|
13
14
|
const HOST = String(process.env.HOST || '127.0.0.1');
|
|
15
|
+
const READONLY = String(process.env.Q_AI_NOTE_READONLY || '').toLowerCase() === 'true';
|
|
16
|
+
const LOCAL_WRITE_ONLY = String(process.env.Q_AI_NOTE_LOCAL_WRITE_ONLY || '').toLowerCase() === 'true';
|
|
14
17
|
let serverInstance = null;
|
|
15
18
|
let dbInitialized = false;
|
|
16
|
-
|
|
19
|
+
function normalizeIpAddress(ip) {
|
|
20
|
+
const raw = String(ip || '').trim();
|
|
21
|
+
if (!raw)
|
|
22
|
+
return '';
|
|
23
|
+
return raw.startsWith('::ffff:') ? raw.slice(7) : raw;
|
|
24
|
+
}
|
|
25
|
+
function buildLocalIpSet() {
|
|
26
|
+
const set = new Set(['127.0.0.1', '::1', 'localhost']);
|
|
27
|
+
const nets = os.networkInterfaces();
|
|
28
|
+
Object.values(nets).forEach((rows) => {
|
|
29
|
+
(rows || []).forEach((row) => {
|
|
30
|
+
if (!row || typeof row.address !== 'string')
|
|
31
|
+
return;
|
|
32
|
+
set.add(normalizeIpAddress(row.address));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
return set;
|
|
36
|
+
}
|
|
37
|
+
export function isReadonlyForIp(ip, policyInput = false, localIpSet = buildLocalIpSet()) {
|
|
38
|
+
const policy = resolveAccessPolicy(policyInput);
|
|
39
|
+
if (policy.readonly)
|
|
40
|
+
return true;
|
|
41
|
+
if (!policy.localWriteOnly)
|
|
42
|
+
return false;
|
|
43
|
+
return !localIpSet.has(normalizeIpAddress(ip));
|
|
44
|
+
}
|
|
45
|
+
function resolveAccessPolicy(input) {
|
|
46
|
+
if (typeof input === 'boolean') {
|
|
47
|
+
return { readonly: input, localWriteOnly: false };
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
readonly: Boolean(input?.readonly),
|
|
51
|
+
localWriteOnly: Boolean(input?.localWriteOnly),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function createApp(inputPolicy = READONLY) {
|
|
55
|
+
const policy = resolveAccessPolicy(inputPolicy);
|
|
56
|
+
const localIpSet = buildLocalIpSet();
|
|
57
|
+
const isReadonlyRequest = (req) => isReadonlyForIp(req.ip || req.socket?.remoteAddress || '', policy, localIpSet);
|
|
17
58
|
const app = express();
|
|
18
59
|
app.use(express.json());
|
|
19
60
|
app.use('/node_modules', express.static(path.join(__dirname, '../../node_modules')));
|
|
20
61
|
app.use(express.static(path.join(__dirname, '../web')));
|
|
62
|
+
app.get('/api/runtime', (req, res) => {
|
|
63
|
+
res.json({ success: true, data: { readonly: isReadonlyRequest(req) } });
|
|
64
|
+
});
|
|
65
|
+
app.use('/api/settings', (req, res, next) => {
|
|
66
|
+
if (!isReadonlyRequest(req))
|
|
67
|
+
return next();
|
|
68
|
+
return res.status(403).json({ success: false, error: 'Readonly mode enabled' });
|
|
69
|
+
});
|
|
70
|
+
app.use('/api', (req, res, next) => {
|
|
71
|
+
if (!isReadonlyRequest(req))
|
|
72
|
+
return next();
|
|
73
|
+
if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS')
|
|
74
|
+
return next();
|
|
75
|
+
return res.status(403).json({ success: false, error: 'Readonly mode enabled' });
|
|
76
|
+
});
|
|
21
77
|
app.use('/api/sandboxes', sandboxRouter);
|
|
22
78
|
app.use('/api/sandboxes/:sandboxId/items', workItemRouter);
|
|
23
79
|
app.use('/api/items', workItemRouter);
|
|
@@ -30,7 +86,7 @@ export function createApp() {
|
|
|
30
86
|
});
|
|
31
87
|
return app;
|
|
32
88
|
}
|
|
33
|
-
export function startServer(port = PORT, host = HOST) {
|
|
89
|
+
export function startServer(port = PORT, host = HOST, policyInput = { readonly: READONLY, localWriteOnly: LOCAL_WRITE_ONLY }) {
|
|
34
90
|
if (serverInstance) {
|
|
35
91
|
return serverInstance;
|
|
36
92
|
}
|
|
@@ -38,10 +94,12 @@ export function startServer(port = PORT, host = HOST) {
|
|
|
38
94
|
initDb();
|
|
39
95
|
dbInitialized = true;
|
|
40
96
|
}
|
|
41
|
-
const
|
|
97
|
+
const policy = resolveAccessPolicy(policyInput);
|
|
98
|
+
const app = createApp(policy);
|
|
42
99
|
serverInstance = app.listen(port, host, () => {
|
|
43
100
|
const printableHost = host === '0.0.0.0' ? 'localhost' : host;
|
|
44
|
-
|
|
101
|
+
const mode = policy.readonly ? 'readonly' : policy.localWriteOnly ? 'local-write-only' : 'readwrite';
|
|
102
|
+
console.log(`Server running at http://${printableHost}:${port} (${mode})`);
|
|
45
103
|
});
|
|
46
104
|
return serverInstance;
|
|
47
105
|
}
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAC7C,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAC/C,OAAO,WAAW,MAAM,gBAAgB,CAAC;AACzC,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAC/C,OAAO,kBAAkB,MAAM,uBAAuB,CAAC;AAEvD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
|