@quantod/qq 1.1.19 → 1.2.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/dist/commands.d.ts +10 -7
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +204 -60
- package/dist/commands.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +56 -4
- package/dist/db.js.map +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +36 -18
- package/dist/http.js.map +1 -1
- package/dist/index.js +22 -9
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +20 -21
- package/dist/mcp.js.map +1 -1
- package/dist/resources/chrome.md +3 -3
- package/dist/resources/guide.md +108 -105
- package/dist/sdk-templates/javascript.js +1 -1
- package/dist/sdk-templates/python.d.ts.map +1 -1
- package/dist/sdk-templates/python.js +5 -2
- package/dist/sdk-templates/python.js.map +1 -1
- package/package.json +1 -1
- package/src/commands.ts +196 -64
- package/src/db.ts +59 -4
- package/src/http.ts +39 -25
- package/src/index.ts +20 -11
- package/src/mcp.ts +21 -22
- package/src/resources/chrome.md +3 -3
- package/src/resources/guide.md +108 -105
- package/src/sdk-templates/javascript.ts +1 -1
- package/src/sdk-templates/python.ts +5 -2
package/src/index.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { join } from 'node:path';
|
|
|
4
4
|
import { dump } from 'js-yaml';
|
|
5
5
|
import {
|
|
6
6
|
makePipeline, listPipelines, deletePipeline, push, claim, release,
|
|
7
|
-
|
|
8
|
-
type FilterOptions, type PushOptions, type ClaimOptions, type ClaimStrategy, type PayloadFormat, type DuplicateMode, type BatchWriteFormat,
|
|
7
|
+
query, status, unstick, uploadFile, uploadData, backup, QQError,
|
|
8
|
+
type FilterOptions, type PushOptions, type ClaimOptions, type ClaimStrategy, type PayloadFormat, type DuplicateMode, type BatchWriteFormat, type PayloadSelect,
|
|
9
9
|
} from './commands.js';
|
|
10
10
|
|
|
11
11
|
function out(data: unknown): void {
|
|
@@ -112,7 +112,7 @@ program
|
|
|
112
112
|
program
|
|
113
113
|
.command('push <path> [id]')
|
|
114
114
|
.option('--priority <n>', 'item priority (higher = claimed first)', parseFloatArg, 0.0)
|
|
115
|
-
.option('--payload-format <yaml|json
|
|
115
|
+
.option('--payload-format <yaml|json>', 'payload parsing format: yaml (default) or json')
|
|
116
116
|
.action((path, id, opts) => {
|
|
117
117
|
try {
|
|
118
118
|
const { pipeline, stage = '' } = parsePath(path);
|
|
@@ -143,7 +143,7 @@ program
|
|
|
143
143
|
.option('--target <stage>', 'move item to this stage (default: stay in current stage)')
|
|
144
144
|
.option('--replace')
|
|
145
145
|
.option('--priority <n>', 'update item priority', parseFloatArg)
|
|
146
|
-
.option('--payload-format <yaml|json
|
|
146
|
+
.option('--payload-format <yaml|json>', 'payload parsing format: yaml (default) or json')
|
|
147
147
|
.action((pipeline, seqStr, opts) => {
|
|
148
148
|
try {
|
|
149
149
|
const seq = parseInt(seqStr, 10);
|
|
@@ -154,15 +154,24 @@ program
|
|
|
154
154
|
} catch (e) { e instanceof QQError ? fail(e.message) : fail(String(e)); }
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
-
addFilterOptions(program.command('
|
|
158
|
-
.option('--
|
|
157
|
+
addFilterOptions(program.command('query <path>'))
|
|
158
|
+
.option('--filter <json>', 'MongoDB-style payload filter as JSON')
|
|
159
|
+
.option('--select <fields>', 'payload fields: * for all, or comma-separated paths (e.g. status,meta.author)')
|
|
159
160
|
.option('--strategy <priority|fifo|lifo|random>', 'sort order (default: fifo)', 'fifo')
|
|
160
161
|
.action((path, opts) => {
|
|
161
162
|
try {
|
|
162
163
|
const { pipeline, stage = '*' } = parsePath(path);
|
|
163
164
|
const filters = collectFilters(opts);
|
|
164
165
|
if (filters.claimed === undefined) filters.claimed = false;
|
|
165
|
-
|
|
166
|
+
if (opts.filter) {
|
|
167
|
+
try { filters.filter = JSON.parse(opts.filter as string); }
|
|
168
|
+
catch { fail('--filter must be valid JSON'); }
|
|
169
|
+
}
|
|
170
|
+
if (opts.select) {
|
|
171
|
+
const s = opts.select as string;
|
|
172
|
+
filters.select = s === '*' ? '*' : s.split(',').map((f: string) => f.trim()).filter(Boolean) as PayloadSelect;
|
|
173
|
+
}
|
|
174
|
+
out(query(pipeline, stage, filters, opts.strategy as ClaimStrategy));
|
|
166
175
|
}
|
|
167
176
|
catch (e) { fail(e instanceof Error ? e.message : String(e)); }
|
|
168
177
|
});
|
|
@@ -186,14 +195,14 @@ addReapFilterOptions(program.command('unstick <path>'))
|
|
|
186
195
|
});
|
|
187
196
|
|
|
188
197
|
program
|
|
189
|
-
.command('
|
|
198
|
+
.command('upload-file <path> <pipeline>')
|
|
190
199
|
.description('Load multiple items from a file into a pipeline (JSONL, JSON array, YAML array, CSV)')
|
|
191
200
|
.option('--stage <stage>', 'default stage for records that do not specify one')
|
|
192
201
|
.option('--delete-after', 'delete the file after loading')
|
|
193
202
|
.option('--duplicates <ignore|append|replace>', 'how to handle duplicate ids: ignore (default), append payload, or replace payload', 'ignore')
|
|
194
203
|
.action((filePath, pipeline, opts) => {
|
|
195
204
|
try {
|
|
196
|
-
const results =
|
|
205
|
+
const results = uploadFile(filePath, pipeline, { stage: opts.stage, deleteAfter: opts.deleteAfter ?? false, duplicates: opts.duplicates as DuplicateMode });
|
|
197
206
|
const counts = { new: 0, duplicate: 0, error: 0 };
|
|
198
207
|
for (const r of results) counts[r.status]++;
|
|
199
208
|
const items = results.map(r => ({ [r.id]: r.message ? `${r.status}: ${r.message}` : r.status }));
|
|
@@ -202,7 +211,7 @@ program
|
|
|
202
211
|
});
|
|
203
212
|
|
|
204
213
|
program
|
|
205
|
-
.command('
|
|
214
|
+
.command('upload-data <pipeline>')
|
|
206
215
|
.description('Write multiple items from stdin into a pipeline (JSONL, JSON array, YAML array, CSV)')
|
|
207
216
|
.requiredOption('--format <jsonl|json|yaml|csv>', 'data format')
|
|
208
217
|
.option('--stage <stage>', 'default stage for records that do not specify one')
|
|
@@ -211,7 +220,7 @@ program
|
|
|
211
220
|
try {
|
|
212
221
|
const data = readStdin();
|
|
213
222
|
if (!data) fail('no data on stdin');
|
|
214
|
-
const results =
|
|
223
|
+
const results = uploadData(data, opts.format as BatchWriteFormat, pipeline, { stage: opts.stage, duplicates: opts.duplicates as DuplicateMode });
|
|
215
224
|
const counts = { new: 0, duplicate: 0, error: 0 };
|
|
216
225
|
for (const r of results) counts[r.status]++;
|
|
217
226
|
const items = results.map(r => ({ [r.id]: r.message ? `${r.status}: ${r.message}` : r.status }));
|
package/src/mcp.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { z } from 'zod';
|
|
|
7
7
|
import { dump } from 'js-yaml';
|
|
8
8
|
import {
|
|
9
9
|
makePipeline, deletePipeline, push, claim, release,
|
|
10
|
-
|
|
10
|
+
query, status, unstick, uploadFile, uploadData, readFile,
|
|
11
11
|
} from './commands.js';
|
|
12
12
|
import { startHttp } from './http.js';
|
|
13
13
|
import { renderJavaScriptSdk } from './sdk-templates/javascript.js';
|
|
@@ -81,12 +81,11 @@ export async function startMcp(fixedPort?: number): Promise<void> {
|
|
|
81
81
|
pipeline: z.string(),
|
|
82
82
|
stage: z.string().optional(),
|
|
83
83
|
id: z.string().optional(),
|
|
84
|
-
payload: z.
|
|
84
|
+
payload: z.record(z.unknown()).optional(),
|
|
85
85
|
priority: z.number().optional(),
|
|
86
|
-
payloadFormat: z.enum(['yaml', 'json', 'text']).optional(),
|
|
87
86
|
},
|
|
88
|
-
}, ({ pipeline, stage, id, payload, priority
|
|
89
|
-
try { return ok(`id: ${push(pipeline, stage, id, { payload, priority
|
|
87
|
+
}, ({ pipeline, stage, id, payload, priority }) => {
|
|
88
|
+
try { return ok(`id: ${push(pipeline, stage, id, { payload, priority })}`); }
|
|
90
89
|
catch (e) { return err(e); }
|
|
91
90
|
});
|
|
92
91
|
|
|
@@ -109,13 +108,12 @@ export async function startMcp(fixedPort?: number): Promise<void> {
|
|
|
109
108
|
pipeline: z.string(),
|
|
110
109
|
seq: z.number(),
|
|
111
110
|
target: z.string().optional(),
|
|
112
|
-
payload: z.
|
|
111
|
+
payload: z.record(z.unknown()).optional(),
|
|
113
112
|
replace: z.boolean().optional(),
|
|
114
113
|
priority: z.number().optional(),
|
|
115
|
-
payloadFormat: z.enum(['yaml', 'json', 'text']).optional(),
|
|
116
114
|
},
|
|
117
|
-
}, ({ pipeline, seq, target, payload, replace, priority
|
|
118
|
-
try { release(pipeline, seq, { target, payload, replace, priority
|
|
115
|
+
}, ({ pipeline, seq, target, payload, replace, priority }) => {
|
|
116
|
+
try { release(pipeline, seq, { target, payload, replace, priority }); return ok('ok: true'); }
|
|
119
117
|
catch (e) { return err(e); }
|
|
120
118
|
});
|
|
121
119
|
|
|
@@ -127,12 +125,13 @@ export async function startMcp(fixedPort?: number): Promise<void> {
|
|
|
127
125
|
catch (e) { return err(e); }
|
|
128
126
|
});
|
|
129
127
|
|
|
130
|
-
server.registerTool('
|
|
131
|
-
description: '
|
|
128
|
+
server.registerTool('query', {
|
|
129
|
+
description: 'Query items from a pipeline stage without claiming. Read guide before using.',
|
|
132
130
|
inputSchema: {
|
|
133
131
|
pipeline: z.string(),
|
|
134
132
|
stage: z.string().optional(),
|
|
135
|
-
|
|
133
|
+
filter: z.record(z.unknown()).optional(),
|
|
134
|
+
select: z.union([z.literal('*'), z.array(z.string())]).optional(),
|
|
136
135
|
claimed: z.boolean().optional(),
|
|
137
136
|
ids: z.array(z.string()).optional(),
|
|
138
137
|
createdAfter: z.union([z.number(), z.string()]).optional(),
|
|
@@ -142,12 +141,12 @@ export async function startMcp(fixedPort?: number): Promise<void> {
|
|
|
142
141
|
offset: z.number().optional(),
|
|
143
142
|
strategy: z.enum(['priority', 'fifo', 'lifo', 'random']).optional(),
|
|
144
143
|
},
|
|
145
|
-
}, ({ pipeline, stage,
|
|
144
|
+
}, ({ pipeline, stage, filter, select, claimed, ids, createdAfter, createdBefore, modifiedAfter, limit, offset, strategy }) => {
|
|
146
145
|
try {
|
|
147
|
-
const items =
|
|
146
|
+
const items = query(pipeline, stage ?? '*', {
|
|
148
147
|
claimed, ids, created_after: createdAfter, created_before: createdBefore,
|
|
149
|
-
modified_after: modifiedAfter, limit, offset,
|
|
150
|
-
},
|
|
148
|
+
modified_after: modifiedAfter, limit, offset, filter, select,
|
|
149
|
+
}, strategy);
|
|
151
150
|
return ok(dump(items, { lineWidth: -1 }));
|
|
152
151
|
} catch (e) { return err(e); }
|
|
153
152
|
});
|
|
@@ -191,8 +190,8 @@ export async function startMcp(fixedPort?: number): Promise<void> {
|
|
|
191
190
|
return ok(snippet);
|
|
192
191
|
});
|
|
193
192
|
|
|
194
|
-
server.registerTool('
|
|
195
|
-
description: '
|
|
193
|
+
server.registerTool('upload_file', {
|
|
194
|
+
description: 'Upload items from a file into a pipeline. Supports JSONL, JSON array, YAML array, CSV. Read guide before using.',
|
|
196
195
|
inputSchema: {
|
|
197
196
|
path: z.string(),
|
|
198
197
|
pipeline: z.string(),
|
|
@@ -202,7 +201,7 @@ export async function startMcp(fixedPort?: number): Promise<void> {
|
|
|
202
201
|
},
|
|
203
202
|
}, ({ path: filePath, pipeline, stage, deleteAfter, duplicates }) => {
|
|
204
203
|
try {
|
|
205
|
-
const results =
|
|
204
|
+
const results = uploadFile(filePath, pipeline, { stage, deleteAfter, duplicates });
|
|
206
205
|
const counts = { new: 0, duplicate: 0, error: 0 };
|
|
207
206
|
for (const r of results) counts[r.status]++;
|
|
208
207
|
const items = results.map(r => ({ [r.id]: r.message ? `${r.status}: ${r.message}` : r.status }));
|
|
@@ -210,8 +209,8 @@ export async function startMcp(fixedPort?: number): Promise<void> {
|
|
|
210
209
|
} catch (e) { return err(e); }
|
|
211
210
|
});
|
|
212
211
|
|
|
213
|
-
server.registerTool('
|
|
214
|
-
description: '
|
|
212
|
+
server.registerTool('upload_data', {
|
|
213
|
+
description: 'Upload multiple items from inline data into a pipeline. Supports JSONL, JSON array, YAML array, CSV. Read guide before using.',
|
|
215
214
|
inputSchema: {
|
|
216
215
|
data: z.string(),
|
|
217
216
|
format: z.enum(['jsonl', 'json', 'yaml', 'csv']),
|
|
@@ -221,7 +220,7 @@ export async function startMcp(fixedPort?: number): Promise<void> {
|
|
|
221
220
|
},
|
|
222
221
|
}, ({ data, format, pipeline, stage, duplicates }) => {
|
|
223
222
|
try {
|
|
224
|
-
const results =
|
|
223
|
+
const results = uploadData(data, format, pipeline, { stage, duplicates });
|
|
225
224
|
const counts = { new: 0, duplicate: 0, error: 0 };
|
|
226
225
|
for (const r of results) counts[r.status]++;
|
|
227
226
|
const items = results.map(r => ({ [r.id]: r.message ? `${r.status}: ${r.message}` : r.status }));
|
package/src/resources/chrome.md
CHANGED
|
@@ -6,7 +6,7 @@ Read the `guide` resource first — this document covers Chrome-specific pattern
|
|
|
6
6
|
|
|
7
7
|
## How to get data out of Chrome
|
|
8
8
|
|
|
9
|
-
Chrome devtools and connectors usually struggle to get large data packages from web pages in Chrome. Solution: download +
|
|
9
|
+
Chrome devtools and connectors usually struggle to get large data packages from web pages in Chrome. Solution: download + upload_file. The browser's download API has no mixed content restrictions. Trigger a download from the HTTPS page, then call the `upload_file` MCP tool to ingest the file. Don't route large data through the context.
|
|
10
10
|
|
|
11
11
|
**In the injected script (HTTPS page):**
|
|
12
12
|
```javascript
|
|
@@ -19,9 +19,9 @@ a.download = 'qq-data.json';
|
|
|
19
19
|
a.click();
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
**Then call
|
|
22
|
+
**Then call upload_file:**
|
|
23
23
|
```
|
|
24
|
-
|
|
24
|
+
upload_file(
|
|
25
25
|
path: "$HOME/Downloads/qq-data.json",
|
|
26
26
|
pipeline: "my_pypeline",
|
|
27
27
|
stage: "inbox",
|