@pikku/cli 0.12.25 → 0.12.27
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/console-app/assets/index-BERGDBO9.js +228 -0
- package/console-app/assets/{index-C52h1B_L.css → index-CQ29NRyR.css} +1 -1
- package/console-app/index.html +2 -2
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +6 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +9 -0
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +140 -125
- package/dist/.pikku/function/pikku-functions.gen.js +3 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +11 -10
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +11 -9
- package/dist/.pikku/schemas/schemas/DbAuditInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PikkuTestsCoverageInput.schema.json +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/cli.wiring.js +8 -0
- package/dist/src/fabric/functions/validate-core.js +6 -6
- package/dist/src/fabric/functions/validate.function.js +1 -1
- package/dist/src/functions/commands/db-audit.d.ts +1 -0
- package/dist/src/functions/commands/db-audit.js +67 -0
- package/dist/src/functions/commands/db-migrate.js +5 -8
- package/dist/src/functions/commands/db-reset.js +9 -8
- package/dist/src/functions/commands/db-seed.js +9 -8
- package/dist/src/functions/commands/db-shared.d.ts +2 -4
- package/dist/src/functions/commands/db-shared.js +15 -5
- package/dist/src/functions/commands/dev.js +14 -8
- package/dist/src/functions/commands/new-addon.js +2 -2
- package/dist/src/functions/commands/tests-coverage.d.ts +3 -0
- package/dist/src/functions/commands/tests-coverage.js +34 -0
- package/dist/src/functions/db/annotation-parser.d.ts +31 -0
- package/dist/src/functions/db/annotation-parser.js +93 -0
- package/dist/src/functions/db/db-codegen.d.ts +24 -0
- package/dist/src/functions/db/db-codegen.js +276 -0
- package/dist/src/functions/db/db-introspector.d.ts +15 -0
- package/dist/src/functions/db/db-introspector.js +1 -0
- package/dist/src/functions/db/db-migrator.d.ts +32 -0
- package/dist/src/functions/db/db-migrator.js +65 -0
- package/dist/src/functions/db/local-db.d.ts +26 -32
- package/dist/src/functions/db/local-db.js +100 -53
- package/dist/src/functions/db/postgres/postgres-introspector.d.ts +10 -0
- package/dist/src/functions/db/postgres/postgres-introspector.js +54 -0
- package/dist/src/functions/db/postgres/postgres-migrator.d.ts +9 -0
- package/dist/src/functions/db/postgres/postgres-migrator.js +32 -0
- package/dist/src/functions/db/sqlite/sqlite-introspector.d.ts +9 -0
- package/dist/src/functions/db/sqlite/sqlite-introspector.js +35 -0
- package/dist/src/functions/db/sqlite/sqlite-migrator.d.ts +10 -0
- package/dist/src/functions/db/sqlite/sqlite-migrator.js +36 -0
- package/dist/src/functions/validate/workspace-validate.js +7 -3
- package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +2 -1
- package/dist/src/functions/wirings/console/serialize-console-functions.js +4 -4
- package/dist/src/functions/wirings/functions/serialize-addon-types.js +1 -1
- package/dist/src/scaffold/rpc-remote.gen.js +1 -1
- package/dist/src/services.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -4
- package/skills/pikku-middleware/SKILL.md +283 -0
- package/skills/pikku-permissions/SKILL.md +165 -0
- package/skills/pikku-security/SKILL.md +38 -177
- package/skills/pikku-tag-middleware/SKILL.md +13 -0
- package/skills/pikku-testing/SKILL.md +208 -0
- package/console-app/assets/index-D4DgafuS.js +0 -232
- package/dist/src/functions/db/sql-migrator.d.ts +0 -26
- package/dist/src/functions/db/sql-migrator.js +0 -104
- package/dist/src/functions/db/sqlite-codegen.d.ts +0 -45
- package/dist/src/functions/db/sqlite-codegen.js +0 -294
- /package/dist/src/functions/db/{seed.d.ts → sqlite/seed.d.ts} +0 -0
- /package/dist/src/functions/db/{seed.js → sqlite/seed.js} +0 -0
- /package/dist/src/functions/db/{sqlite-kysely.d.ts → sqlite/sqlite-kysely.d.ts} +0 -0
- /package/dist/src/functions/db/{sqlite-kysely.js → sqlite/sqlite-kysely.js} +0 -0
- /package/dist/src/functions/db/{sqlite-runtime-bun.d.ts → sqlite/sqlite-runtime-bun.d.ts} +0 -0
- /package/dist/src/functions/db/{sqlite-runtime-bun.js → sqlite/sqlite-runtime-bun.js} +0 -0
- /package/dist/src/functions/db/{sqlite-runtime-node.d.ts → sqlite/sqlite-runtime-node.d.ts} +0 -0
- /package/dist/src/functions/db/{sqlite-runtime-node.js → sqlite/sqlite-runtime-node.js} +0 -0
- /package/dist/src/functions/db/{sqlite-runtime.d.ts → sqlite/sqlite-runtime.d.ts} +0 -0
- /package/dist/src/functions/db/{sqlite-runtime.js → sqlite/sqlite-runtime.js} +0 -0
|
@@ -1,48 +1,42 @@
|
|
|
1
1
|
import type { Kysely } from 'kysely';
|
|
2
|
-
import { type MigrateResult } from './
|
|
3
|
-
import { type CodegenResult } from './
|
|
2
|
+
import { type MigrateResult } from './db-migrator.js';
|
|
3
|
+
import { type CodegenResult } from './db-codegen.js';
|
|
4
4
|
import { type ZodCodegenResult } from './zod-codegen.js';
|
|
5
|
-
import { type SeedResult } from './seed.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
export interface ResolvedLocalDb {
|
|
10
|
-
dbFile: string;
|
|
11
|
-
runtimeDir: string;
|
|
5
|
+
import { type SeedResult } from './sqlite/seed.js';
|
|
6
|
+
import type { UserConfigShape } from '../commands/db-shared.js';
|
|
7
|
+
interface ResolvedDbBase {
|
|
12
8
|
migrationsDir: string;
|
|
13
9
|
seedFile: string;
|
|
14
10
|
schemaFile: string;
|
|
15
11
|
coercionFile: string;
|
|
12
|
+
manifestFile: string;
|
|
16
13
|
zodFile: string;
|
|
17
14
|
camelCase: boolean;
|
|
18
15
|
}
|
|
16
|
+
export interface ResolvedSqliteDb extends ResolvedDbBase {
|
|
17
|
+
dialect: 'sqlite';
|
|
18
|
+
dbFile: string;
|
|
19
|
+
runtimeDir: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ResolvedPostgresDb extends ResolvedDbBase {
|
|
22
|
+
dialect: 'postgres';
|
|
23
|
+
connectionString: string;
|
|
24
|
+
}
|
|
25
|
+
export type ResolvedDb = ResolvedSqliteDb | ResolvedPostgresDb;
|
|
19
26
|
/**
|
|
20
|
-
* Resolve a
|
|
21
|
-
*
|
|
22
|
-
* - schema/coercion/zod are generated into outDir/db
|
|
23
|
-
* - migrations and seed are authored source under rootDir/db
|
|
27
|
+
* Resolve a UserConfigShape into an absolute-path descriptor.
|
|
28
|
+
* Returns null when neither sqliteDb nor postgresUrl is configured.
|
|
24
29
|
*/
|
|
25
|
-
export declare function
|
|
30
|
+
export declare function resolveDb(userConfig: UserConfigShape, rootDir: string, outDir: string, runtimeDir?: string): ResolvedDb | null;
|
|
31
|
+
/** @deprecated Use resolveDb(userConfig, ...) instead. */
|
|
32
|
+
export declare function resolveLocalDb(sqliteDb: string | undefined, rootDir: string, outDir: string, runtimeDir?: string): ResolvedSqliteDb | null;
|
|
26
33
|
export interface MigrateAndCodegenOutcome {
|
|
27
34
|
migrate: MigrateResult;
|
|
28
35
|
codegen: CodegenResult;
|
|
29
36
|
zod: ZodCodegenResult;
|
|
30
37
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
export
|
|
36
|
-
export declare function seed(resolved: ResolvedLocalDb): Promise<SeedResult>;
|
|
37
|
-
/**
|
|
38
|
-
* Delete the dev DB file. Refuses if NODE_ENV is 'production' or the
|
|
39
|
-
* resolved file lives outside the project root (defensive against
|
|
40
|
-
* misconfigured absolute paths).
|
|
41
|
-
*/
|
|
42
|
-
export declare function reset(resolved: ResolvedLocalDb, rootDir: string): void;
|
|
43
|
-
/**
|
|
44
|
-
* Construct the user-facing Kysely instance for the dev DB. Used by
|
|
45
|
-
* `pikku dev` to populate inMemoryServices.kysely.
|
|
46
|
-
* Wires the coercion plugin when db/coercion.gen.ts exists.
|
|
47
|
-
*/
|
|
48
|
-
export declare function createKysely<DB>(resolved: ResolvedLocalDb): Promise<Kysely<DB>>;
|
|
38
|
+
export declare function migrateAndCodegen(resolved: ResolvedDb): Promise<MigrateAndCodegenOutcome>;
|
|
39
|
+
export declare function seed(resolved: ResolvedSqliteDb): Promise<SeedResult>;
|
|
40
|
+
export declare function reset(resolved: ResolvedSqliteDb, rootDir: string): void;
|
|
41
|
+
export declare function createKysely<DB>(resolved: ResolvedSqliteDb): Promise<Kysely<DB>>;
|
|
42
|
+
export {};
|
|
@@ -1,65 +1,122 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, rmSync } from 'node:fs';
|
|
2
2
|
import { resolve, isAbsolute, relative, dirname, join } from 'node:path';
|
|
3
|
-
import { migrate } from './
|
|
4
|
-
import { generateSchemaTypes } from './
|
|
3
|
+
import { migrate } from './db-migrator.js';
|
|
4
|
+
import { generateSchemaTypes } from './db-codegen.js';
|
|
5
5
|
import { generateZodTypes } from './zod-codegen.js';
|
|
6
|
-
import { seed as runSeed } from './seed.js';
|
|
7
6
|
import { createCoercionPlugin } from './coercion-plugin.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { SqliteMigrationExecutor } from './sqlite/sqlite-migrator.js';
|
|
8
|
+
import { SqliteIntrospector } from './sqlite/sqlite-introspector.js';
|
|
9
|
+
import { createSqliteKysely } from './sqlite/sqlite-kysely.js';
|
|
10
|
+
import { loadSqliteRuntime } from './sqlite/sqlite-runtime.js';
|
|
11
|
+
import { seed as runSeed } from './sqlite/seed.js';
|
|
12
|
+
import { PostgresMigrationExecutor } from './postgres/postgres-migrator.js';
|
|
13
|
+
import { PostgresIntrospector } from './postgres/postgres-introspector.js';
|
|
14
|
+
// ─── Resolution ───────────────────────────────────────────────────────────────
|
|
10
15
|
/**
|
|
11
|
-
* Resolve a
|
|
12
|
-
*
|
|
13
|
-
* - schema/coercion/zod are generated into outDir/db
|
|
14
|
-
* - migrations and seed are authored source under rootDir/db
|
|
16
|
+
* Resolve a UserConfigShape into an absolute-path descriptor.
|
|
17
|
+
* Returns null when neither sqliteDb nor postgresUrl is configured.
|
|
15
18
|
*/
|
|
16
|
-
export function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const resolvedRuntimeDir = runtimeDir ?? join(rootDir, '.pikku-runtime');
|
|
21
|
-
return {
|
|
22
|
-
dbFile: file
|
|
23
|
-
? resolveAgainst(rootDir, file)
|
|
24
|
-
: join(resolvedRuntimeDir, 'dev.db'),
|
|
25
|
-
runtimeDir: resolvedRuntimeDir,
|
|
26
|
-
migrationsDir: resolveAgainst(rootDir, 'db/migrations'),
|
|
27
|
-
seedFile: resolveAgainst(rootDir, 'db/seed.sql'),
|
|
19
|
+
export function resolveDb(userConfig, rootDir, outDir, runtimeDir) {
|
|
20
|
+
const base = (sub, seedFileName) => ({
|
|
21
|
+
migrationsDir: resolveAgainst(rootDir, sub),
|
|
22
|
+
seedFile: resolveAgainst(rootDir, seedFileName),
|
|
28
23
|
schemaFile: join(outDir, 'db', 'schema.d.ts'),
|
|
29
24
|
coercionFile: join(outDir, 'db', 'coercion.gen.ts'),
|
|
25
|
+
manifestFile: join(outDir, 'db', 'classification.gen.ts'),
|
|
30
26
|
zodFile: join(outDir, 'db', 'zod.gen.ts'),
|
|
31
27
|
camelCase: true,
|
|
32
|
-
};
|
|
28
|
+
});
|
|
29
|
+
if (userConfig.postgresUrl && userConfig.sqliteDb) {
|
|
30
|
+
throw new Error('Both postgresUrl and sqliteDb are set. Configure exactly one database dialect.');
|
|
31
|
+
}
|
|
32
|
+
if (userConfig.postgresUrl) {
|
|
33
|
+
return {
|
|
34
|
+
dialect: 'postgres',
|
|
35
|
+
connectionString: userConfig.postgresUrl,
|
|
36
|
+
...base('db/postgres', 'db/postgres-seed.sql'),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (userConfig.sqliteDb) {
|
|
40
|
+
const resolvedRuntimeDir = runtimeDir
|
|
41
|
+
? resolveAgainst(rootDir, runtimeDir)
|
|
42
|
+
: join(rootDir, '.pikku-runtime');
|
|
43
|
+
return {
|
|
44
|
+
dialect: 'sqlite',
|
|
45
|
+
dbFile: resolveAgainst(rootDir, userConfig.sqliteDb),
|
|
46
|
+
runtimeDir: resolvedRuntimeDir,
|
|
47
|
+
...base('db/sqlite', 'db/sqlite-seed.sql'),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/** @deprecated Use resolveDb(userConfig, ...) instead. */
|
|
53
|
+
export function resolveLocalDb(sqliteDb, rootDir, outDir, runtimeDir) {
|
|
54
|
+
if (!sqliteDb)
|
|
55
|
+
return null;
|
|
56
|
+
const result = resolveDb({ sqliteDb }, rootDir, outDir, runtimeDir);
|
|
57
|
+
return result;
|
|
33
58
|
}
|
|
34
59
|
function resolveAgainst(root, p) {
|
|
35
60
|
return isAbsolute(p) ? p : resolve(root, p);
|
|
36
61
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Run the migrate routine (open → tracking-table → drift-check → apply →
|
|
39
|
-
* codegen → close). Used by both `pikku db migrate` and `pikku dev` boot.
|
|
40
|
-
*/
|
|
41
62
|
export async function migrateAndCodegen(resolved) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
63
|
+
if (resolved.dialect === 'sqlite') {
|
|
64
|
+
mkdirSync(dirname(resolved.dbFile), { recursive: true });
|
|
65
|
+
const runtime = await loadSqliteRuntime();
|
|
66
|
+
const db = runtime.open(resolved.dbFile);
|
|
67
|
+
try {
|
|
68
|
+
const executor = new SqliteMigrationExecutor(db);
|
|
69
|
+
const migrateResult = await migrate(executor, resolved.migrationsDir);
|
|
70
|
+
const introspector = new SqliteIntrospector(db);
|
|
71
|
+
const codegenResult = await generateSchemaTypes(introspector, {
|
|
72
|
+
outFile: resolved.schemaFile,
|
|
73
|
+
coercionFile: resolved.coercionFile,
|
|
74
|
+
manifestFile: resolved.manifestFile,
|
|
75
|
+
camelCase: resolved.camelCase,
|
|
76
|
+
migrationsDir: resolved.migrationsDir,
|
|
77
|
+
});
|
|
78
|
+
const zodResult = generateZodTypes({
|
|
79
|
+
schemaFile: resolved.schemaFile,
|
|
80
|
+
outFile: resolved.zodFile,
|
|
81
|
+
});
|
|
82
|
+
return { migrate: migrateResult, codegen: codegenResult, zod: zodResult };
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
db.close();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Postgres
|
|
89
|
+
const introspector = new PostgresIntrospector(resolved.connectionString);
|
|
90
|
+
await introspector.connect();
|
|
45
91
|
try {
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
92
|
+
const { Client } = await import('pg');
|
|
93
|
+
const client = new Client({ connectionString: resolved.connectionString });
|
|
94
|
+
await client.connect();
|
|
95
|
+
try {
|
|
96
|
+
const executor = new PostgresMigrationExecutor(client);
|
|
97
|
+
const migrateResult = await migrate(executor, resolved.migrationsDir);
|
|
98
|
+
const codegenResult = await generateSchemaTypes(introspector, {
|
|
99
|
+
outFile: resolved.schemaFile,
|
|
100
|
+
coercionFile: resolved.coercionFile,
|
|
101
|
+
manifestFile: resolved.manifestFile,
|
|
102
|
+
camelCase: resolved.camelCase,
|
|
103
|
+
migrationsDir: resolved.migrationsDir,
|
|
104
|
+
});
|
|
105
|
+
const zodResult = generateZodTypes({
|
|
106
|
+
schemaFile: resolved.schemaFile,
|
|
107
|
+
outFile: resolved.zodFile,
|
|
108
|
+
});
|
|
109
|
+
return { migrate: migrateResult, codegen: codegenResult, zod: zodResult };
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
await client.end();
|
|
113
|
+
}
|
|
58
114
|
}
|
|
59
115
|
finally {
|
|
60
|
-
|
|
116
|
+
await introspector.close();
|
|
61
117
|
}
|
|
62
118
|
}
|
|
119
|
+
// ─── SQLite-only operations ───────────────────────────────────────────────────
|
|
63
120
|
export async function seed(resolved) {
|
|
64
121
|
const runtime = await loadSqliteRuntime();
|
|
65
122
|
const db = runtime.open(resolved.dbFile);
|
|
@@ -70,28 +127,18 @@ export async function seed(resolved) {
|
|
|
70
127
|
db.close();
|
|
71
128
|
}
|
|
72
129
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Delete the dev DB file. Refuses if NODE_ENV is 'production' or the
|
|
75
|
-
* resolved file lives outside the project root (defensive against
|
|
76
|
-
* misconfigured absolute paths).
|
|
77
|
-
*/
|
|
78
130
|
export function reset(resolved, rootDir) {
|
|
79
131
|
if (process.env.NODE_ENV === 'production') {
|
|
80
132
|
throw new Error(`pikku db reset refused: NODE_ENV=production. This command only runs in dev.`);
|
|
81
133
|
}
|
|
82
134
|
const rel = relative(resolved.runtimeDir, resolved.dbFile);
|
|
83
135
|
if (rel.startsWith('..') || isAbsolute(rel)) {
|
|
84
|
-
throw new Error(`pikku db reset refused: resolved DB file (${resolved.dbFile}) is outside the runtime directory (${resolved.runtimeDir}). Override
|
|
136
|
+
throw new Error(`pikku db reset refused: resolved DB file (${resolved.dbFile}) is outside the runtime directory (${resolved.runtimeDir}). Override sqliteDb or set runtimeDir correctly.`);
|
|
85
137
|
}
|
|
86
138
|
if (existsSync(resolved.dbFile)) {
|
|
87
139
|
rmSync(resolved.dbFile);
|
|
88
140
|
}
|
|
89
141
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Construct the user-facing Kysely instance for the dev DB. Used by
|
|
92
|
-
* `pikku dev` to populate inMemoryServices.kysely.
|
|
93
|
-
* Wires the coercion plugin when db/coercion.gen.ts exists.
|
|
94
|
-
*/
|
|
95
142
|
export async function createKysely(resolved) {
|
|
96
143
|
mkdirSync(dirname(resolved.dbFile), { recursive: true });
|
|
97
144
|
const runtime = await loadSqliteRuntime();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Client } from 'pg';
|
|
2
|
+
import type { DbIntrospector, ColumnInfo } from '../db-introspector.js';
|
|
3
|
+
export declare class PostgresIntrospector implements DbIntrospector {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(connectionStringOrClient: string | Client);
|
|
6
|
+
connect(): Promise<void>;
|
|
7
|
+
listTables(): Promise<string[]>;
|
|
8
|
+
getColumns(table: string): Promise<ColumnInfo[]>;
|
|
9
|
+
close(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Client } from 'pg';
|
|
2
|
+
export class PostgresIntrospector {
|
|
3
|
+
client;
|
|
4
|
+
constructor(connectionStringOrClient) {
|
|
5
|
+
this.client =
|
|
6
|
+
typeof connectionStringOrClient === 'string'
|
|
7
|
+
? new Client({ connectionString: connectionStringOrClient })
|
|
8
|
+
: connectionStringOrClient;
|
|
9
|
+
}
|
|
10
|
+
async connect() {
|
|
11
|
+
await this.client.connect();
|
|
12
|
+
}
|
|
13
|
+
async listTables() {
|
|
14
|
+
const result = await this.client.query(`SELECT table_name
|
|
15
|
+
FROM information_schema.tables
|
|
16
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
|
17
|
+
ORDER BY table_name`);
|
|
18
|
+
return result.rows.map((r) => r.table_name);
|
|
19
|
+
}
|
|
20
|
+
async getColumns(table) {
|
|
21
|
+
const result = await this.client.query(`SELECT
|
|
22
|
+
c.column_name,
|
|
23
|
+
c.data_type,
|
|
24
|
+
c.is_nullable,
|
|
25
|
+
c.column_default,
|
|
26
|
+
c.is_generated,
|
|
27
|
+
EXISTS(
|
|
28
|
+
SELECT 1
|
|
29
|
+
FROM information_schema.table_constraints tc
|
|
30
|
+
JOIN information_schema.key_column_usage kcu
|
|
31
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
32
|
+
AND tc.table_schema = kcu.table_schema
|
|
33
|
+
AND tc.table_name = kcu.table_name
|
|
34
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
35
|
+
AND tc.table_schema = 'public'
|
|
36
|
+
AND tc.table_name = $1
|
|
37
|
+
AND kcu.column_name = c.column_name
|
|
38
|
+
) AS is_pk
|
|
39
|
+
FROM information_schema.columns c
|
|
40
|
+
WHERE c.table_schema = 'public' AND c.table_name = $1
|
|
41
|
+
ORDER BY c.ordinal_position`, [table]);
|
|
42
|
+
return result.rows.map((r) => ({
|
|
43
|
+
name: r.column_name,
|
|
44
|
+
type: r.data_type,
|
|
45
|
+
notNull: r.is_nullable === 'NO',
|
|
46
|
+
pk: Boolean(r.is_pk),
|
|
47
|
+
defaultValue: r.column_default,
|
|
48
|
+
generated: r.is_generated === 'ALWAYS',
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
async close() {
|
|
52
|
+
await this.client.end();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Client } from 'pg';
|
|
2
|
+
import type { MigrationExecutor, AppliedMigration } from '../db-migrator.js';
|
|
3
|
+
export declare class PostgresMigrationExecutor implements MigrationExecutor {
|
|
4
|
+
private readonly client;
|
|
5
|
+
constructor(client: Client);
|
|
6
|
+
ensureTrackingTable(): Promise<void>;
|
|
7
|
+
getApplied(): Promise<AppliedMigration[]>;
|
|
8
|
+
runMigration(sql: string, name: string, hash: string): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const TRACKING_TABLE = 'sql_migrations';
|
|
2
|
+
export class PostgresMigrationExecutor {
|
|
3
|
+
client;
|
|
4
|
+
constructor(client) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
}
|
|
7
|
+
async ensureTrackingTable() {
|
|
8
|
+
await this.client.query(`
|
|
9
|
+
CREATE TABLE IF NOT EXISTS ${TRACKING_TABLE} (
|
|
10
|
+
name TEXT PRIMARY KEY,
|
|
11
|
+
hash TEXT NOT NULL,
|
|
12
|
+
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
13
|
+
)
|
|
14
|
+
`);
|
|
15
|
+
}
|
|
16
|
+
async getApplied() {
|
|
17
|
+
const { rows } = await this.client.query(`SELECT name, hash, applied_at FROM ${TRACKING_TABLE} ORDER BY name`);
|
|
18
|
+
return rows;
|
|
19
|
+
}
|
|
20
|
+
async runMigration(sql, name, hash) {
|
|
21
|
+
await this.client.query('BEGIN');
|
|
22
|
+
try {
|
|
23
|
+
await this.client.query(sql);
|
|
24
|
+
await this.client.query(`INSERT INTO ${TRACKING_TABLE} (name, hash) VALUES ($1, $2)`, [name, hash]);
|
|
25
|
+
await this.client.query('COMMIT');
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
await this.client.query('ROLLBACK');
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DbIntrospector, ColumnInfo } from '../db-introspector.js';
|
|
2
|
+
import type { SyncSqliteDatabase } from './sqlite-runtime.js';
|
|
3
|
+
export declare class SqliteIntrospector implements DbIntrospector {
|
|
4
|
+
private readonly db;
|
|
5
|
+
constructor(db: SyncSqliteDatabase);
|
|
6
|
+
listTables(): Promise<string[]>;
|
|
7
|
+
getColumns(table: string): Promise<ColumnInfo[]>;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const SKIP_TABLES = new Set(['sqlite_sequence', 'sql_migrations']);
|
|
2
|
+
export class SqliteIntrospector {
|
|
3
|
+
db;
|
|
4
|
+
constructor(db) {
|
|
5
|
+
this.db = db;
|
|
6
|
+
}
|
|
7
|
+
async listTables() {
|
|
8
|
+
const rows = this.db
|
|
9
|
+
.prepare(`SELECT name FROM sqlite_master
|
|
10
|
+
WHERE type = 'table'
|
|
11
|
+
AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\'
|
|
12
|
+
ORDER BY name`)
|
|
13
|
+
.all();
|
|
14
|
+
return rows.map((r) => r.name).filter((n) => !SKIP_TABLES.has(n));
|
|
15
|
+
}
|
|
16
|
+
async getColumns(table) {
|
|
17
|
+
const escaped = `"${table.replace(/"/g, '""')}"`;
|
|
18
|
+
const rows = this.db
|
|
19
|
+
.prepare(`PRAGMA table_xinfo(${escaped})`)
|
|
20
|
+
.all();
|
|
21
|
+
return rows
|
|
22
|
+
.filter((c) => c.hidden !== 1)
|
|
23
|
+
.map((c) => ({
|
|
24
|
+
name: c.name,
|
|
25
|
+
type: c.type,
|
|
26
|
+
notNull: Boolean(c.notnull),
|
|
27
|
+
pk: c.pk > 0,
|
|
28
|
+
defaultValue: c.dflt_value != null ? String(c.dflt_value) : null,
|
|
29
|
+
generated: c.hidden === 2 || c.hidden === 3,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
async close() {
|
|
33
|
+
this.db.close();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MigrationExecutor, AppliedMigration } from '../db-migrator.js';
|
|
2
|
+
import type { SyncSqliteDatabase } from './sqlite-runtime.js';
|
|
3
|
+
export declare class SqliteMigrationExecutor implements MigrationExecutor {
|
|
4
|
+
private readonly db;
|
|
5
|
+
constructor(db: SyncSqliteDatabase);
|
|
6
|
+
ensureTrackingTable(): Promise<void>;
|
|
7
|
+
getApplied(): Promise<AppliedMigration[]>;
|
|
8
|
+
runMigration(sql: string, name: string, hash: string): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare function dropTrackingTable(db: SyncSqliteDatabase): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const TRACKING_TABLE = 'sql_migrations';
|
|
2
|
+
export class SqliteMigrationExecutor {
|
|
3
|
+
db;
|
|
4
|
+
constructor(db) {
|
|
5
|
+
this.db = db;
|
|
6
|
+
}
|
|
7
|
+
async ensureTrackingTable() {
|
|
8
|
+
this.db.exec(`CREATE TABLE IF NOT EXISTS ${TRACKING_TABLE} (
|
|
9
|
+
name TEXT PRIMARY KEY,
|
|
10
|
+
hash TEXT NOT NULL,
|
|
11
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
12
|
+
)`);
|
|
13
|
+
}
|
|
14
|
+
async getApplied() {
|
|
15
|
+
return this.db
|
|
16
|
+
.prepare(`SELECT name, hash, applied_at FROM ${TRACKING_TABLE} ORDER BY name`)
|
|
17
|
+
.all();
|
|
18
|
+
}
|
|
19
|
+
async runMigration(sql, name, hash) {
|
|
20
|
+
this.db.exec('BEGIN');
|
|
21
|
+
try {
|
|
22
|
+
this.db.exec(sql);
|
|
23
|
+
this.db
|
|
24
|
+
.prepare(`INSERT INTO ${TRACKING_TABLE} (name, hash) VALUES (?, ?)`)
|
|
25
|
+
.run(name, hash);
|
|
26
|
+
this.db.exec('COMMIT');
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
this.db.exec('ROLLBACK');
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function dropTrackingTable(db) {
|
|
35
|
+
db.exec(`DROP TABLE IF EXISTS ${TRACKING_TABLE}`);
|
|
36
|
+
}
|
|
@@ -96,7 +96,6 @@ export async function runWorkspaceValidate(startDir = process.cwd()) {
|
|
|
96
96
|
info('pikku-config-no-client-files', 'pikku.config.json missing "clientFiles" — no generated SDK or React Query hooks', pikkuConfigPath, 'Add clientFiles.rpcMapDeclarationFile and clientFiles.reactQueryFile pointing to packages/functions-sdk/src/pikku/');
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
const hasConfiguredDevDb = Boolean(pikkuConfig?.dev?.db);
|
|
100
99
|
const rootPkgPath = join(root, 'package.json');
|
|
101
100
|
const rootPkg = await readJsonSafe(rootPkgPath);
|
|
102
101
|
if (!rootPkg) {
|
|
@@ -143,8 +142,13 @@ export async function runWorkspaceValidate(startDir = process.cwd()) {
|
|
|
143
142
|
if (!servicesText) {
|
|
144
143
|
w('services-missing', 'packages/functions/src/services.ts not found', servicesPath, 'Create services.ts and export your service factory for the workspace');
|
|
145
144
|
}
|
|
146
|
-
const migrationsDir = join(fnDir, 'db', 'migrations');
|
|
147
145
|
const authEnabled = await hasAuthSessionMiddleware(fnDir);
|
|
146
|
+
const configText = await readTextSafe(join(fnDir, 'src', 'config.ts'));
|
|
147
|
+
const hasConfiguredDevDb = /sqliteDb/.test(configText ?? '');
|
|
148
|
+
const hasPostgresUrl = /postgresUrl/.test(configText ?? '');
|
|
149
|
+
const migrationsDir = hasPostgresUrl
|
|
150
|
+
? join(fnDir, 'db', 'postgres')
|
|
151
|
+
: join(fnDir, 'db', 'sqlite');
|
|
148
152
|
let createsAppUser = false;
|
|
149
153
|
let createsAuthVerificationToken = false;
|
|
150
154
|
if (existsSync(migrationsDir)) {
|
|
@@ -178,7 +182,7 @@ export async function runWorkspaceValidate(startDir = process.cwd()) {
|
|
|
178
182
|
}
|
|
179
183
|
}
|
|
180
184
|
if (authEnabled && !hasConfiguredDevDb) {
|
|
181
|
-
e('auth-dev-db-missing', 'Auth middleware is registered, but
|
|
185
|
+
e('auth-dev-db-missing', 'Auth middleware is registered, but createConfig is missing sqliteDb so local auth schema validation and db migrate cannot run', pikkuConfigPath, 'Add sqliteDb to createConfig so `pikku db migrate` can create and validate the local auth schema');
|
|
182
186
|
}
|
|
183
187
|
if (authEnabled && !createsAppUser) {
|
|
184
188
|
e('auth-schema-missing-app-user', 'Auth middleware is registered, but no SQL migration creates the app_user table', migrationsDir, 'Add a migration that creates app_user before enabling auth');
|
|
@@ -19,7 +19,7 @@ export const agentCaller = pikkuSessionlessFunc<
|
|
|
19
19
|
})
|
|
20
20
|
|
|
21
21
|
export const agentStreamCaller = pikkuSessionlessFunc<
|
|
22
|
-
{ agentName: string; message: string; threadId: string; resourceId: string },
|
|
22
|
+
{ agentName: string; message: string; threadId: string; resourceId: string; context?: string },
|
|
23
23
|
void
|
|
24
24
|
>({
|
|
25
25
|
tags: ['pikku'],
|
|
@@ -29,6 +29,7 @@ export const agentStreamCaller = pikkuSessionlessFunc<
|
|
|
29
29
|
message: data.message,
|
|
30
30
|
threadId: data.threadId,
|
|
31
31
|
resourceId: data.resourceId,
|
|
32
|
+
...(data.context ? { context: data.context } : {}),
|
|
32
33
|
})
|
|
33
34
|
},
|
|
34
35
|
})
|
|
@@ -12,7 +12,7 @@ export const pikkuConsoleSetSecret = pikkuSessionlessFunc<{
|
|
|
12
12
|
expose: true,
|
|
13
13
|
auth: false,
|
|
14
14
|
func: async ({ secrets }, { secretId, value }) => {
|
|
15
|
-
await secrets.
|
|
15
|
+
await secrets.setSecret(secretId, value)
|
|
16
16
|
return { success: true }
|
|
17
17
|
},
|
|
18
18
|
})
|
|
@@ -31,7 +31,7 @@ export const pikkuConsoleGetVariable = pikkuSessionlessFunc<
|
|
|
31
31
|
return { exists: false, value: null }
|
|
32
32
|
}
|
|
33
33
|
try {
|
|
34
|
-
const value = await variables.
|
|
34
|
+
const value = await variables.get(variableId)
|
|
35
35
|
return { exists: true, value }
|
|
36
36
|
} catch {
|
|
37
37
|
const value = await variables.get(variableId)
|
|
@@ -52,7 +52,7 @@ export const pikkuConsoleSetVariable = pikkuSessionlessFunc<
|
|
|
52
52
|
if (typeof value === 'string') {
|
|
53
53
|
await variables.set(variableId, value)
|
|
54
54
|
} else {
|
|
55
|
-
await variables.
|
|
55
|
+
await variables.set(variableId, value)
|
|
56
56
|
}
|
|
57
57
|
return { success: true }
|
|
58
58
|
},
|
|
@@ -85,7 +85,7 @@ export const pikkuConsoleGetSecret = pikkuSessionlessFunc<
|
|
|
85
85
|
if (!exists) {
|
|
86
86
|
return { exists: false, value: null }
|
|
87
87
|
}
|
|
88
|
-
const value = await secrets.
|
|
88
|
+
const value = await secrets.getSecret(secretId)
|
|
89
89
|
return { exists: true, value }
|
|
90
90
|
},
|
|
91
91
|
})
|
|
@@ -52,7 +52,7 @@ export const pikkuAddonConfig = <ExistingServices extends Omit<Partial<Singleton
|
|
|
52
52
|
* config,
|
|
53
53
|
* { secrets }
|
|
54
54
|
* ) => {
|
|
55
|
-
* const creds = await secrets.
|
|
55
|
+
* const creds = await secrets.getSecret<GithubCredentials>('GITHUB_CREDENTIALS')
|
|
56
56
|
* const github = new GithubService(creds)
|
|
57
57
|
* return { github }
|
|
58
58
|
* })
|
package/dist/src/services.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { pikkuCLIRender } from '@pikku/core/cli';
|
|
2
2
|
import { LocalVariablesService, LocalSecretService, LogLevel, InMemoryWorkflowService, } from '@pikku/core/services';
|
|
3
|
+
import { NoopAuditService } from '@pikku/core';
|
|
3
4
|
import { CLILogger } from './services/cli-logger.service.js';
|
|
4
5
|
import { getPikkuCLIConfig } from './utils/pikku-cli-config.js';
|
|
5
6
|
import { inspect, serializeInspectorState, deserializeInspectorState, filterInspectorState, getInitialInspectorState, ErrorCode, } from '@pikku/inspector';
|
|
@@ -251,6 +252,7 @@ export const createSingletonServices = async (config) => {
|
|
|
251
252
|
logger,
|
|
252
253
|
variables,
|
|
253
254
|
secrets: new LocalSecretService(variables),
|
|
255
|
+
audit: new NoopAuditService(),
|
|
254
256
|
getInspectorState,
|
|
255
257
|
workflowService,
|
|
256
258
|
};
|