@dinoxx/dinox-cli 1.0.1 → 1.0.3
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 +241 -6
- package/dist/commands/auth/index.js +10 -17
- package/dist/commands/config/index.d.ts +1 -0
- package/dist/commands/config/index.js +4 -17
- package/dist/commands/notes/index.js +9 -1
- package/dist/commands/notes/repo.d.ts +3 -0
- package/dist/commands/notes/repo.js +64 -4
- package/dist/commands/notes/searchSql.d.ts +11 -0
- package/dist/commands/notes/searchSql.js +599 -0
- package/dist/commands/prompt/index.js +54 -1
- package/dist/commands/prompt/repo.d.ts +12 -0
- package/dist/commands/prompt/repo.js +146 -0
- package/dist/commands/sync.js +5 -6
- package/dist/config/keys.d.ts +3 -3
- package/dist/config/keys.js +1 -4
- package/dist/config/paths.js +2 -1
- package/dist/powersync/syncPass.d.ts +7 -0
- package/dist/powersync/syncPass.js +10 -0
- package/package.json +3 -1
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
import { generateId } from '../../utils/id.js';
|
|
2
|
+
import { DinoxError } from '../../utils/errors.js';
|
|
3
|
+
function normalizeText(value) {
|
|
4
|
+
return value.trim();
|
|
5
|
+
}
|
|
6
|
+
function normalizeLookup(value) {
|
|
7
|
+
return normalizeText(value).toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
async function getPromptColumns(db) {
|
|
10
|
+
const rows = await db.getAll('PRAGMA table_info(c_cmd)', []);
|
|
11
|
+
return new Set(rows.map((row) => row.name).filter(Boolean));
|
|
12
|
+
}
|
|
13
|
+
async function listPromptRowsForWrite(db, columnNames) {
|
|
14
|
+
const isDelExpr = columnNames.has('is_del') ? 'is_del' : 'NULL';
|
|
15
|
+
const userIdExpr = columnNames.has('user_id') ? `TRIM(COALESCE(user_id, ''))` : `''`;
|
|
16
|
+
const activeFirstExpr = columnNames.has('is_del')
|
|
17
|
+
? 'CASE WHEN is_del IS NULL OR is_del = 0 THEN 0 ELSE 1 END'
|
|
18
|
+
: '0';
|
|
19
|
+
const updateOrderExpr = columnNames.has('update_at') ? 'update_at DESC,' : '';
|
|
20
|
+
const insertOrderExpr = columnNames.has('insert_at') ? 'insert_at DESC,' : '';
|
|
21
|
+
return db.getAll(`
|
|
22
|
+
SELECT
|
|
23
|
+
TRIM(COALESCE(id, '')) AS id,
|
|
24
|
+
TRIM(COALESCE(name, '')) AS name,
|
|
25
|
+
TRIM(COALESCE(cmd, '')) AS cmd,
|
|
26
|
+
${isDelExpr} AS is_del,
|
|
27
|
+
${userIdExpr} AS user_id
|
|
28
|
+
FROM c_cmd
|
|
29
|
+
WHERE TRIM(COALESCE(id, '')) <> ''
|
|
30
|
+
ORDER BY
|
|
31
|
+
${activeFirstExpr} ASC,
|
|
32
|
+
${updateOrderExpr}
|
|
33
|
+
${insertOrderExpr}
|
|
34
|
+
id DESC
|
|
35
|
+
`, []);
|
|
36
|
+
}
|
|
1
37
|
export async function listPrompts(db) {
|
|
2
38
|
const rows = await db.getAll(`
|
|
3
39
|
SELECT DISTINCT
|
|
@@ -16,3 +52,113 @@ export async function listPrompts(db) {
|
|
|
16
52
|
}))
|
|
17
53
|
.filter((row) => row.name.length > 0 && row.cmd.length > 0);
|
|
18
54
|
}
|
|
55
|
+
export async function addPrompt(db, input) {
|
|
56
|
+
const name = normalizeText(input.name);
|
|
57
|
+
if (!name) {
|
|
58
|
+
throw new DinoxError('Prompt name is required');
|
|
59
|
+
}
|
|
60
|
+
const cmd = normalizeText(input.cmd);
|
|
61
|
+
if (!cmd) {
|
|
62
|
+
throw new DinoxError('Prompt cmd is required');
|
|
63
|
+
}
|
|
64
|
+
const userId = normalizeText(input.userId);
|
|
65
|
+
const nameLookup = normalizeLookup(name);
|
|
66
|
+
const cmdLookup = normalizeLookup(cmd);
|
|
67
|
+
const columnNames = await getPromptColumns(db);
|
|
68
|
+
const requiredColumns = ['id', 'name', 'cmd'];
|
|
69
|
+
const missingRequired = requiredColumns.filter((column) => !columnNames.has(column));
|
|
70
|
+
if (missingRequired.length > 0) {
|
|
71
|
+
throw new DinoxError(`c_cmd table is missing required columns: ${missingRequired.join(', ')}`);
|
|
72
|
+
}
|
|
73
|
+
if (columnNames.has('user_id') && !userId) {
|
|
74
|
+
throw new DinoxError('c_cmd insert requires user_id. Missing persisted userId. Run `dino auth login` first.');
|
|
75
|
+
}
|
|
76
|
+
const rows = await listPromptRowsForWrite(db, columnNames);
|
|
77
|
+
const samePromptRows = rows.filter((row) => {
|
|
78
|
+
return normalizeLookup(row.name ?? '') === nameLookup && normalizeLookup(row.cmd ?? '') === cmdLookup;
|
|
79
|
+
});
|
|
80
|
+
const activeRows = samePromptRows.filter((row) => row.is_del !== 1);
|
|
81
|
+
if (activeRows.length > 0) {
|
|
82
|
+
const ids = activeRows.map((row) => row.id?.trim() ?? '').filter(Boolean);
|
|
83
|
+
throw new DinoxError(`Prompt already exists: ${name}`, {
|
|
84
|
+
details: {
|
|
85
|
+
type: 'prompt_exists',
|
|
86
|
+
name,
|
|
87
|
+
cmd,
|
|
88
|
+
ids,
|
|
89
|
+
question: 'This prompt already exists. Do you want to keep using the existing prompt?',
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const timestamp = new Date().toISOString();
|
|
94
|
+
const deletedRow = samePromptRows.find((row) => (row.id?.trim() ?? '').length > 0);
|
|
95
|
+
if (deletedRow?.id) {
|
|
96
|
+
const setParts = [];
|
|
97
|
+
const params = [];
|
|
98
|
+
const setValue = (column, value) => {
|
|
99
|
+
if (!columnNames.has(column)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
setParts.push(`${column} = ?`);
|
|
103
|
+
params.push(value);
|
|
104
|
+
};
|
|
105
|
+
setValue('name', name);
|
|
106
|
+
setValue('cmd', cmd);
|
|
107
|
+
setValue('is_del', 0);
|
|
108
|
+
setValue('update_at', timestamp);
|
|
109
|
+
setValue('user_id', userId);
|
|
110
|
+
if (setParts.length > 0) {
|
|
111
|
+
params.push(deletedRow.id);
|
|
112
|
+
await db.execute(`
|
|
113
|
+
UPDATE c_cmd
|
|
114
|
+
SET ${setParts.join(', ')}
|
|
115
|
+
WHERE id = ?
|
|
116
|
+
`, params);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
id: deletedRow.id,
|
|
120
|
+
name,
|
|
121
|
+
cmd,
|
|
122
|
+
restored: true,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const id = generateId();
|
|
126
|
+
const insertEntries = [];
|
|
127
|
+
const pushInsert = (column, value) => {
|
|
128
|
+
if (!columnNames.has(column)) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
insertEntries.push([column, value]);
|
|
132
|
+
};
|
|
133
|
+
pushInsert('id', id);
|
|
134
|
+
pushInsert('name', name);
|
|
135
|
+
pushInsert('cmd', cmd);
|
|
136
|
+
pushInsert('zone', '');
|
|
137
|
+
pushInsert('type', 'custom');
|
|
138
|
+
pushInsert('priority', 0);
|
|
139
|
+
pushInsert('is_show', 1);
|
|
140
|
+
pushInsert('description', '');
|
|
141
|
+
pushInsert('extra_data', '{}');
|
|
142
|
+
pushInsert('user_id', userId);
|
|
143
|
+
pushInsert('insert_at', timestamp);
|
|
144
|
+
pushInsert('update_at', timestamp);
|
|
145
|
+
pushInsert('is_del', 0);
|
|
146
|
+
pushInsert('template_id', '');
|
|
147
|
+
pushInsert('meta_data', '{}');
|
|
148
|
+
pushInsert('output', 'markdown');
|
|
149
|
+
pushInsert('output_param', '{}');
|
|
150
|
+
pushInsert('category', '');
|
|
151
|
+
const insertColumns = insertEntries.map(([column]) => column);
|
|
152
|
+
const placeholders = insertColumns.map(() => '?').join(', ');
|
|
153
|
+
const insertParams = insertEntries.map(([, value]) => value);
|
|
154
|
+
await db.execute(`
|
|
155
|
+
INSERT INTO c_cmd (${insertColumns.join(', ')})
|
|
156
|
+
VALUES (${placeholders})
|
|
157
|
+
`, insertParams);
|
|
158
|
+
return {
|
|
159
|
+
id,
|
|
160
|
+
name,
|
|
161
|
+
cmd,
|
|
162
|
+
restored: false,
|
|
163
|
+
};
|
|
164
|
+
}
|
package/dist/commands/sync.js
CHANGED
|
@@ -2,8 +2,8 @@ import { loadConfig } from '../config/store.js';
|
|
|
2
2
|
import { resolveConfig } from '../config/resolve.js';
|
|
3
3
|
import { DinoxError } from '../utils/errors.js';
|
|
4
4
|
import { printYaml } from '../utils/output.js';
|
|
5
|
-
import { connectPowerSync, getStatusSnapshot
|
|
6
|
-
import {
|
|
5
|
+
import { connectPowerSync, getStatusSnapshot } from '../powersync/runtime.js';
|
|
6
|
+
import { runSingleSyncPass } from '../powersync/syncPass.js';
|
|
7
7
|
function parseSyncTimeoutMs(value) {
|
|
8
8
|
if (typeof value !== 'string') {
|
|
9
9
|
return undefined;
|
|
@@ -29,16 +29,15 @@ export function registerSyncCommand(program) {
|
|
|
29
29
|
timeoutMs,
|
|
30
30
|
});
|
|
31
31
|
try {
|
|
32
|
-
const
|
|
33
|
-
const tokenIndex = await syncNoteTokenIndex(db);
|
|
32
|
+
const syncPass = await runSingleSyncPass(db, timeoutMs);
|
|
34
33
|
const status = getStatusSnapshot(db);
|
|
35
34
|
const payload = {
|
|
36
35
|
ok: true,
|
|
37
36
|
dbPath,
|
|
38
37
|
stale,
|
|
39
|
-
idle,
|
|
38
|
+
idle: syncPass.idle,
|
|
40
39
|
uploadEnabled: Boolean(config.powersync.uploadBaseUrl),
|
|
41
|
-
tokenIndex,
|
|
40
|
+
tokenIndex: syncPass.tokenIndex,
|
|
42
41
|
status,
|
|
43
42
|
};
|
|
44
43
|
if (jsonOutput) {
|
package/dist/config/keys.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export declare const CONFIG_KEYS: readonly ["
|
|
2
|
-
export declare const FIXED_CONFIG_KEYS: readonly [
|
|
3
|
-
export declare const READABLE_CONFIG_KEYS: readonly ["
|
|
1
|
+
export declare const CONFIG_KEYS: readonly ["sync.timeoutMs"];
|
|
2
|
+
export declare const FIXED_CONFIG_KEYS: readonly [];
|
|
3
|
+
export declare const READABLE_CONFIG_KEYS: readonly ["sync.timeoutMs"];
|
|
4
4
|
export type ConfigKey = (typeof CONFIG_KEYS)[number];
|
|
5
5
|
export type FixedConfigKey = (typeof FIXED_CONFIG_KEYS)[number];
|
|
6
6
|
export type ReadableConfigKey = (typeof READABLE_CONFIG_KEYS)[number];
|
package/dist/config/keys.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
export const CONFIG_KEYS = [
|
|
2
|
-
'powersync.tokenEndpoint',
|
|
3
|
-
'powersync.uploadV4Path',
|
|
4
|
-
'powersync.uploadV2Path',
|
|
5
2
|
'sync.timeoutMs',
|
|
6
3
|
];
|
|
7
|
-
export const FIXED_CONFIG_KEYS = [
|
|
4
|
+
export const FIXED_CONFIG_KEYS = [];
|
|
8
5
|
export const READABLE_CONFIG_KEYS = [...CONFIG_KEYS, ...FIXED_CONFIG_KEYS];
|
|
9
6
|
export function isConfigKey(value) {
|
|
10
7
|
return CONFIG_KEYS.includes(value);
|
package/dist/config/paths.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
const POWERSYNC_DB_FILENAME = 'dinox-cli.sqlite';
|
|
3
4
|
function resolveConfigHome() {
|
|
4
5
|
if (process.env.DINOX_CONFIG_DIR) {
|
|
5
6
|
return process.env.DINOX_CONFIG_DIR;
|
|
@@ -35,5 +36,5 @@ export function getDataDir() {
|
|
|
35
36
|
return path.join(resolveDataHome(), 'dinox');
|
|
36
37
|
}
|
|
37
38
|
export function getPowerSyncDbPath() {
|
|
38
|
-
return path.join(getDataDir(),
|
|
39
|
+
return path.join(getDataDir(), POWERSYNC_DB_FILENAME);
|
|
39
40
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AbstractPowerSyncDatabase } from '@powersync/common';
|
|
2
|
+
import { type NoteTokenIndexResult } from './tokenIndex.js';
|
|
3
|
+
export type SyncPassResult = {
|
|
4
|
+
idle: boolean;
|
|
5
|
+
tokenIndex: NoteTokenIndexResult;
|
|
6
|
+
};
|
|
7
|
+
export declare function runSingleSyncPass(db: AbstractPowerSyncDatabase, timeoutMs: number): Promise<SyncPassResult>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { waitForIdleDataFlow } from './runtime.js';
|
|
2
|
+
import { syncNoteTokenIndex } from './tokenIndex.js';
|
|
3
|
+
export async function runSingleSyncPass(db, timeoutMs) {
|
|
4
|
+
const idle = await waitForIdleDataFlow(db, Math.min(timeoutMs, 10_000));
|
|
5
|
+
const tokenIndex = await syncNoteTokenIndex(db);
|
|
6
|
+
return {
|
|
7
|
+
idle,
|
|
8
|
+
tokenIndex,
|
|
9
|
+
};
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dinoxx/dinox-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Dinox CLI",
|
|
5
5
|
"main": "dist/dinox.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsc -p tsconfig.json && node scripts/add-shebang.mjs",
|
|
8
8
|
"dev": "tsx src/dinox.ts",
|
|
9
9
|
"prepublishOnly": "npm run build",
|
|
10
|
+
"publish:npm": "sh scripts/publish-npm.sh",
|
|
11
|
+
"publish:npm:dry-run": "sh scripts/publish-npm.sh --dry-run",
|
|
10
12
|
"start": "node dist/dinox.js",
|
|
11
13
|
"test": "node --test --import tsx test/**/*.test.ts"
|
|
12
14
|
},
|