@agentic-kit/pi 0.2.1 → 0.5.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/esm/host.d.ts +17 -0
- package/esm/index.d.ts +4 -1
- package/esm/index.js +3 -0
- package/esm/provision-database/create-database-provision.d.ts +1 -1
- package/esm/provision-database/manifest.d.ts +10 -0
- package/esm/provision-database/manifest.js +48 -0
- package/esm/provision-database/presets.d.ts +4 -0
- package/esm/provision-database/presets.js +13 -0
- package/esm/provision-database/resolve.d.ts +27 -0
- package/esm/provision-database/resolve.js +49 -0
- package/esm/tools/provision-database.js +21 -2
- package/host.d.ts +17 -0
- package/index.d.ts +4 -1
- package/index.js +12 -1
- package/package.json +6 -5
- package/provision-database/create-database-provision.d.ts +1 -1
- package/provision-database/manifest.d.ts +10 -0
- package/provision-database/manifest.js +56 -0
- package/provision-database/presets.d.ts +4 -0
- package/provision-database/presets.js +17 -0
- package/provision-database/resolve.d.ts +27 -0
- package/provision-database/resolve.js +53 -0
- package/tools/provision-database.js +21 -2
- package/esm/provision-database/modules.d.ts +0 -2
- package/esm/provision-database/modules.js +0 -40
- package/provision-database/modules.d.ts +0 -2
- package/provision-database/modules.js +0 -43
package/esm/host.d.ts
CHANGED
|
@@ -38,6 +38,16 @@ export type PreviewToken = {
|
|
|
38
38
|
accessTokenExpiresAt?: string;
|
|
39
39
|
userId?: string;
|
|
40
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* Overlay layered over the pinned base preset when provisioning a database.
|
|
43
|
+
* Structurally the `ProvisionOverlay` from `provision-database/resolve`; typed
|
|
44
|
+
* loosely here to keep `host.ts` free of provision-internal imports.
|
|
45
|
+
*/
|
|
46
|
+
export interface HostProvisionOverlay {
|
|
47
|
+
preset?: string;
|
|
48
|
+
add?: (string | [string, Record<string, unknown>])[];
|
|
49
|
+
remove?: string[];
|
|
50
|
+
}
|
|
41
51
|
export interface PiToolsHost {
|
|
42
52
|
/** Signed-in platform account, or null/undefined when signed out. */
|
|
43
53
|
account(): HostAccount | null | undefined;
|
|
@@ -49,6 +59,13 @@ export interface PiToolsHost {
|
|
|
49
59
|
previewToken?(): Promise<PreviewToken | null>;
|
|
50
60
|
/** Treat tokens expiring within this window as already expired. Default 30s. */
|
|
51
61
|
dataTokenSkewMs?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Optional provision overlay: pick a base preset and/or layer module
|
|
64
|
+
* add/remove on top of it. The base module list always comes from the pinned
|
|
65
|
+
* `node-type-registry` preset — this only customizes it. Distributed as data
|
|
66
|
+
* (e.g. materialized from appstash / a pinned git ref), never as code.
|
|
67
|
+
*/
|
|
68
|
+
provisionOverlay?(): HostProvisionOverlay | null | undefined | Promise<HostProvisionOverlay | null | undefined>;
|
|
52
69
|
}
|
|
53
70
|
export declare const DEFAULT_DATA_TOKEN_SKEW_MS = 30000;
|
|
54
71
|
export declare function configureHost(host: PiToolsHost): void;
|
package/esm/index.d.ts
CHANGED
|
@@ -5,7 +5,10 @@ export declare const dbTools: ExtensionFactory;
|
|
|
5
5
|
export declare function createDbTools(host: PiToolsHost): ExtensionFactory;
|
|
6
6
|
export { type ConfirmGate, type ConfirmGateDeps, createConfirmGate } from './confirm-gate';
|
|
7
7
|
export { type ModulesClient, type ProjectContext, resolveDataToken, resolveProjectContext, } from './context';
|
|
8
|
-
export { type ActiveDataToken, configureHost, type DataAuthBroker, getHost, type HostAccount, type HostBackendConfig, type PiToolsHost, type PreviewToken, } from './host';
|
|
8
|
+
export { type ActiveDataToken, configureHost, type DataAuthBroker, getHost, type HostAccount, type HostBackendConfig, type HostProvisionOverlay, type PiToolsHost, type PreviewToken, } from './host';
|
|
9
|
+
export { loadProvisionManifest, parseProvisionManifest, PROVISION_MANIFEST_FILE, type ProvisionManifest, } from './provision-database/manifest';
|
|
10
|
+
export { allModulePresets, DEFAULT_PROVISION_PRESET, getModulePreset, type ModulePreset, type ProvisionModule, } from './provision-database/presets';
|
|
11
|
+
export { moduleKey, type ProvisionOverlay, resolveProvisionModules, } from './provision-database/resolve';
|
|
9
12
|
export { toolSchema } from './tool-schema';
|
|
10
13
|
export { createTemplatePreviewTables } from './tools/templates';
|
|
11
14
|
export default dbTools;
|
package/esm/index.js
CHANGED
|
@@ -43,6 +43,9 @@ export function createDbTools(host) {
|
|
|
43
43
|
export { createConfirmGate } from './confirm-gate';
|
|
44
44
|
export { resolveDataToken, resolveProjectContext, } from './context';
|
|
45
45
|
export { configureHost, getHost, } from './host';
|
|
46
|
+
export { loadProvisionManifest, parseProvisionManifest, PROVISION_MANIFEST_FILE, } from './provision-database/manifest';
|
|
47
|
+
export { allModulePresets, DEFAULT_PROVISION_PRESET, getModulePreset, } from './provision-database/presets';
|
|
48
|
+
export { moduleKey, resolveProvisionModules, } from './provision-database/resolve';
|
|
46
49
|
export { toolSchema } from './tool-schema';
|
|
47
50
|
export { createTemplatePreviewTables } from './tools/templates';
|
|
48
51
|
export default dbTools;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ProvisionOverlay } from './resolve';
|
|
2
|
+
export declare const PROVISION_MANIFEST_FILE = "provision.json";
|
|
3
|
+
export type ProvisionManifest = ProvisionOverlay;
|
|
4
|
+
/** Validate an untrusted value as a provision manifest, throwing on bad shape. */
|
|
5
|
+
export declare function parseProvisionManifest(value: unknown): ProvisionManifest;
|
|
6
|
+
/**
|
|
7
|
+
* Load `<dir>/provision.json` as an overlay, or `null` when absent. A malformed
|
|
8
|
+
* file throws so a broken overlay is surfaced rather than silently ignored.
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadProvisionManifest(dir: string): Promise<ProvisionManifest | null>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// `provision.json` overlay manifest.
|
|
2
|
+
//
|
|
3
|
+
// A pure-data overlay, distributed the same way skills are: written into a
|
|
4
|
+
// config/app directory (locally, or materialized there from appstash / a pinned
|
|
5
|
+
// git ref) and read at provision time. It NEVER installs or executes a package
|
|
6
|
+
// — the base preset is a pinned build-time dependency; this file only selects a
|
|
7
|
+
// preset and layers add/remove on top.
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
export const PROVISION_MANIFEST_FILE = 'provision.json';
|
|
12
|
+
const ModuleSchema = z.union([
|
|
13
|
+
z.string(),
|
|
14
|
+
z.tuple([z.string(), z.record(z.string(), z.unknown())]),
|
|
15
|
+
]);
|
|
16
|
+
const ProvisionManifestSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
preset: z.string().optional(),
|
|
19
|
+
add: z.array(ModuleSchema).optional(),
|
|
20
|
+
remove: z.array(z.string()).optional(),
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
/** Validate an untrusted value as a provision manifest, throwing on bad shape. */
|
|
24
|
+
export function parseProvisionManifest(value) {
|
|
25
|
+
return ProvisionManifestSchema.parse(value);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Load `<dir>/provision.json` as an overlay, or `null` when absent. A malformed
|
|
29
|
+
* file throws so a broken overlay is surfaced rather than silently ignored.
|
|
30
|
+
*/
|
|
31
|
+
export async function loadProvisionManifest(dir) {
|
|
32
|
+
const file = path.join(dir, PROVISION_MANIFEST_FILE);
|
|
33
|
+
let raw;
|
|
34
|
+
try {
|
|
35
|
+
raw = await readFile(file, 'utf8');
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(raw);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw new Error(`Invalid ${PROVISION_MANIFEST_FILE} at ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
46
|
+
}
|
|
47
|
+
return parseProvisionManifest(parsed);
|
|
48
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { allModulePresets, getModulePreset, type ModulePreset } from 'node-type-registry';
|
|
2
|
+
export type ProvisionModule = string | [string, Record<string, unknown>];
|
|
3
|
+
export declare const DEFAULT_PROVISION_PRESET: string;
|
|
4
|
+
export { allModulePresets, getModulePreset, type ModulePreset };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Provision module presets — the canonical source of truth.
|
|
2
|
+
//
|
|
3
|
+
// The module list is NEVER hand-copied here. It is imported from the pinned
|
|
4
|
+
// `node-type-registry` package (the same presets the backend and dashboard use),
|
|
5
|
+
// so bumping that dependency is how the base module set moves. Hosts customize
|
|
6
|
+
// on top with an ordered overlay (see `resolve.ts`) rather than editing a list.
|
|
7
|
+
import { allModulePresets, getModulePreset, PresetB2bStorage, } from 'node-type-registry';
|
|
8
|
+
// Default base preset. `b2b:storage` = the full multi-tenant B2B shape plus file
|
|
9
|
+
// storage, matching Constructive's app shape. Crucially it already includes
|
|
10
|
+
// `config_secrets_module` — the dependency the old hand-copied list dropped,
|
|
11
|
+
// which made the backend reject provisioning with `REQUIRES (config_secrets_module:app)`.
|
|
12
|
+
export const DEFAULT_PROVISION_PRESET = PresetB2bStorage.name;
|
|
13
|
+
export { allModulePresets, getModulePreset };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ProvisionModule } from './presets';
|
|
2
|
+
export interface ProvisionOverlay {
|
|
3
|
+
/**
|
|
4
|
+
* Base preset name to resolve from (e.g. 'b2b:storage', 'full'). The last
|
|
5
|
+
* layer that sets `preset` wins; if none set it, `DEFAULT_PROVISION_PRESET`
|
|
6
|
+
* is used.
|
|
7
|
+
*/
|
|
8
|
+
preset?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Modules to add. An entry whose key matches an existing module OVERRIDES it
|
|
11
|
+
* (e.g. re-declaring `storage_module` with app-specific options); a new key
|
|
12
|
+
* is appended.
|
|
13
|
+
*/
|
|
14
|
+
add?: ProvisionModule[];
|
|
15
|
+
/**
|
|
16
|
+
* Module names or `name:scope` keys to drop from the base. A bare name
|
|
17
|
+
* removes every scope of that module; `name:scope` removes just that one.
|
|
18
|
+
*/
|
|
19
|
+
remove?: string[];
|
|
20
|
+
}
|
|
21
|
+
/** Stable identity for a module entry: `name` or `name:scope` when scoped. */
|
|
22
|
+
export declare function moduleKey(m: ProvisionModule): string;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the effective module list from a pinned preset plus ordered overlays.
|
|
25
|
+
* Pure: no I/O, no host access. Later overlays win (last-write-wins by key).
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveProvisionModules(layers?: ProvisionOverlay[]): ProvisionModule[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Ordered-overlay resolution for provision modules.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors the skills overlay model (`@agentic-kit/harness` skills manifest): a
|
|
4
|
+
// pinned base (a named preset from `node-type-registry`) is layered under zero
|
|
5
|
+
// or more overlays, later layers winning by module key. An empty overlay list
|
|
6
|
+
// resolves to the base preset unchanged.
|
|
7
|
+
import { allModulePresets, DEFAULT_PROVISION_PRESET, getModulePreset, } from './presets';
|
|
8
|
+
function moduleName(m) {
|
|
9
|
+
return Array.isArray(m) ? m[0] : m;
|
|
10
|
+
}
|
|
11
|
+
function moduleScope(m) {
|
|
12
|
+
if (!Array.isArray(m))
|
|
13
|
+
return undefined;
|
|
14
|
+
const scope = m[1]?.scope;
|
|
15
|
+
return typeof scope === 'string' ? scope : undefined;
|
|
16
|
+
}
|
|
17
|
+
/** Stable identity for a module entry: `name` or `name:scope` when scoped. */
|
|
18
|
+
export function moduleKey(m) {
|
|
19
|
+
const scope = moduleScope(m);
|
|
20
|
+
return scope ? `${moduleName(m)}:${scope}` : moduleName(m);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the effective module list from a pinned preset plus ordered overlays.
|
|
24
|
+
* Pure: no I/O, no host access. Later overlays win (last-write-wins by key).
|
|
25
|
+
*/
|
|
26
|
+
export function resolveProvisionModules(layers = []) {
|
|
27
|
+
const presetName = [...layers].reverse().find((l) => l.preset)?.preset ?? DEFAULT_PROVISION_PRESET;
|
|
28
|
+
const preset = getModulePreset(presetName);
|
|
29
|
+
if (!preset) {
|
|
30
|
+
const known = allModulePresets.map((p) => p.name).join(', ');
|
|
31
|
+
throw new Error(`Unknown provision preset "${presetName}". Known presets: ${known}.`);
|
|
32
|
+
}
|
|
33
|
+
// Ordered map keyed by module identity preserves position while allowing
|
|
34
|
+
// overrides (same key) and appends (new key).
|
|
35
|
+
const resolved = new Map();
|
|
36
|
+
for (const m of preset.modules)
|
|
37
|
+
resolved.set(moduleKey(m), m);
|
|
38
|
+
for (const layer of layers) {
|
|
39
|
+
for (const target of layer.remove ?? []) {
|
|
40
|
+
for (const key of [...resolved.keys()]) {
|
|
41
|
+
if (key === target || key.split(':')[0] === target)
|
|
42
|
+
resolved.delete(key);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (const m of layer.add ?? [])
|
|
46
|
+
resolved.set(moduleKey(m), m);
|
|
47
|
+
}
|
|
48
|
+
return [...resolved.values()];
|
|
49
|
+
}
|
|
@@ -9,8 +9,9 @@ import { probeDatabase } from '../db-probe';
|
|
|
9
9
|
import { createDatabaseProvision } from '../provision-database/create-database-provision';
|
|
10
10
|
import { selectProvisionCredential } from '../provision-database/credential';
|
|
11
11
|
import { archiveBindingKeys, ARCHIVED_BINDING_KEYS, mergeEnv, provisionEnvVars, } from '../provision-database/env-file';
|
|
12
|
-
import {
|
|
12
|
+
import { loadProvisionManifest } from '../provision-database/manifest';
|
|
13
13
|
import { applySqlFixups } from '../provision-database/pg-fixups';
|
|
14
|
+
import { resolveProvisionModules } from '../provision-database/resolve';
|
|
14
15
|
const DEFAULT_API_ENDPOINT = 'http://api.localhost:3000/graphql';
|
|
15
16
|
const DEFAULT_MODULES_ENDPOINT = 'http://modules.localhost:3000/graphql';
|
|
16
17
|
// The per-DB endpoints are subdomain.<domain>; the provisioning domain is the api
|
|
@@ -186,6 +187,24 @@ export const provisionDatabaseTool = {
|
|
|
186
187
|
ok: false,
|
|
187
188
|
out: err instanceof Error ? err.message : String(err),
|
|
188
189
|
}));
|
|
190
|
+
// Resolve the module set: pinned base preset from node-type-registry, then
|
|
191
|
+
// ordered overlays (host default < project-local provision.json). No list is
|
|
192
|
+
// hand-copied here; overlays are pure data. A bad preset/manifest fails the
|
|
193
|
+
// run rather than provisioning a wrong module set.
|
|
194
|
+
let modules;
|
|
195
|
+
try {
|
|
196
|
+
const layers = [];
|
|
197
|
+
const hostOverlay = await host.provisionOverlay?.();
|
|
198
|
+
if (hostOverlay)
|
|
199
|
+
layers.push(hostOverlay);
|
|
200
|
+
const projectOverlay = await loadProvisionManifest(ctx.cwd);
|
|
201
|
+
if (projectOverlay)
|
|
202
|
+
layers.push(projectOverlay);
|
|
203
|
+
modules = resolveProvisionModules(layers);
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
return fail(`Cannot resolve provision modules: ${err instanceof Error ? err.message : String(err)}`);
|
|
207
|
+
}
|
|
189
208
|
const physicalDb = process.env.CONSTRUCTIVE_DB || 'constructive';
|
|
190
209
|
// Provision on the MODULES endpoint: createDatabaseProvisionModule creates
|
|
191
210
|
// the database owned by ownerId, provisions the module set, and bootstraps
|
|
@@ -200,7 +219,7 @@ export const provisionDatabaseTool = {
|
|
|
200
219
|
databaseName,
|
|
201
220
|
domain: provisionDomain(apiEndpoint),
|
|
202
221
|
ownerId,
|
|
203
|
-
modules
|
|
222
|
+
modules,
|
|
204
223
|
})));
|
|
205
224
|
}
|
|
206
225
|
catch (err) {
|
package/host.d.ts
CHANGED
|
@@ -38,6 +38,16 @@ export type PreviewToken = {
|
|
|
38
38
|
accessTokenExpiresAt?: string;
|
|
39
39
|
userId?: string;
|
|
40
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* Overlay layered over the pinned base preset when provisioning a database.
|
|
43
|
+
* Structurally the `ProvisionOverlay` from `provision-database/resolve`; typed
|
|
44
|
+
* loosely here to keep `host.ts` free of provision-internal imports.
|
|
45
|
+
*/
|
|
46
|
+
export interface HostProvisionOverlay {
|
|
47
|
+
preset?: string;
|
|
48
|
+
add?: (string | [string, Record<string, unknown>])[];
|
|
49
|
+
remove?: string[];
|
|
50
|
+
}
|
|
41
51
|
export interface PiToolsHost {
|
|
42
52
|
/** Signed-in platform account, or null/undefined when signed out. */
|
|
43
53
|
account(): HostAccount | null | undefined;
|
|
@@ -49,6 +59,13 @@ export interface PiToolsHost {
|
|
|
49
59
|
previewToken?(): Promise<PreviewToken | null>;
|
|
50
60
|
/** Treat tokens expiring within this window as already expired. Default 30s. */
|
|
51
61
|
dataTokenSkewMs?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Optional provision overlay: pick a base preset and/or layer module
|
|
64
|
+
* add/remove on top of it. The base module list always comes from the pinned
|
|
65
|
+
* `node-type-registry` preset — this only customizes it. Distributed as data
|
|
66
|
+
* (e.g. materialized from appstash / a pinned git ref), never as code.
|
|
67
|
+
*/
|
|
68
|
+
provisionOverlay?(): HostProvisionOverlay | null | undefined | Promise<HostProvisionOverlay | null | undefined>;
|
|
52
69
|
}
|
|
53
70
|
export declare const DEFAULT_DATA_TOKEN_SKEW_MS = 30000;
|
|
54
71
|
export declare function configureHost(host: PiToolsHost): void;
|
package/index.d.ts
CHANGED
|
@@ -5,7 +5,10 @@ export declare const dbTools: ExtensionFactory;
|
|
|
5
5
|
export declare function createDbTools(host: PiToolsHost): ExtensionFactory;
|
|
6
6
|
export { type ConfirmGate, type ConfirmGateDeps, createConfirmGate } from './confirm-gate';
|
|
7
7
|
export { type ModulesClient, type ProjectContext, resolveDataToken, resolveProjectContext, } from './context';
|
|
8
|
-
export { type ActiveDataToken, configureHost, type DataAuthBroker, getHost, type HostAccount, type HostBackendConfig, type PiToolsHost, type PreviewToken, } from './host';
|
|
8
|
+
export { type ActiveDataToken, configureHost, type DataAuthBroker, getHost, type HostAccount, type HostBackendConfig, type HostProvisionOverlay, type PiToolsHost, type PreviewToken, } from './host';
|
|
9
|
+
export { loadProvisionManifest, parseProvisionManifest, PROVISION_MANIFEST_FILE, type ProvisionManifest, } from './provision-database/manifest';
|
|
10
|
+
export { allModulePresets, DEFAULT_PROVISION_PRESET, getModulePreset, type ModulePreset, type ProvisionModule, } from './provision-database/presets';
|
|
11
|
+
export { moduleKey, type ProvisionOverlay, resolveProvisionModules, } from './provision-database/resolve';
|
|
9
12
|
export { toolSchema } from './tool-schema';
|
|
10
13
|
export { createTemplatePreviewTables } from './tools/templates';
|
|
11
14
|
export default dbTools;
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createTemplatePreviewTables = exports.toolSchema = exports.getHost = exports.configureHost = exports.resolveProjectContext = exports.resolveDataToken = exports.createConfirmGate = exports.dbTools = void 0;
|
|
3
|
+
exports.createTemplatePreviewTables = exports.toolSchema = exports.resolveProvisionModules = exports.moduleKey = exports.getModulePreset = exports.DEFAULT_PROVISION_PRESET = exports.allModulePresets = exports.PROVISION_MANIFEST_FILE = exports.parseProvisionManifest = exports.loadProvisionManifest = exports.getHost = exports.configureHost = exports.resolveProjectContext = exports.resolveDataToken = exports.createConfirmGate = exports.dbTools = void 0;
|
|
4
4
|
exports.createDbTools = createDbTools;
|
|
5
5
|
const confirm_gate_1 = require("./confirm-gate");
|
|
6
6
|
const context_1 = require("./context");
|
|
@@ -53,6 +53,17 @@ Object.defineProperty(exports, "resolveProjectContext", { enumerable: true, get:
|
|
|
53
53
|
var host_2 = require("./host");
|
|
54
54
|
Object.defineProperty(exports, "configureHost", { enumerable: true, get: function () { return host_2.configureHost; } });
|
|
55
55
|
Object.defineProperty(exports, "getHost", { enumerable: true, get: function () { return host_2.getHost; } });
|
|
56
|
+
var manifest_1 = require("./provision-database/manifest");
|
|
57
|
+
Object.defineProperty(exports, "loadProvisionManifest", { enumerable: true, get: function () { return manifest_1.loadProvisionManifest; } });
|
|
58
|
+
Object.defineProperty(exports, "parseProvisionManifest", { enumerable: true, get: function () { return manifest_1.parseProvisionManifest; } });
|
|
59
|
+
Object.defineProperty(exports, "PROVISION_MANIFEST_FILE", { enumerable: true, get: function () { return manifest_1.PROVISION_MANIFEST_FILE; } });
|
|
60
|
+
var presets_1 = require("./provision-database/presets");
|
|
61
|
+
Object.defineProperty(exports, "allModulePresets", { enumerable: true, get: function () { return presets_1.allModulePresets; } });
|
|
62
|
+
Object.defineProperty(exports, "DEFAULT_PROVISION_PRESET", { enumerable: true, get: function () { return presets_1.DEFAULT_PROVISION_PRESET; } });
|
|
63
|
+
Object.defineProperty(exports, "getModulePreset", { enumerable: true, get: function () { return presets_1.getModulePreset; } });
|
|
64
|
+
var resolve_1 = require("./provision-database/resolve");
|
|
65
|
+
Object.defineProperty(exports, "moduleKey", { enumerable: true, get: function () { return resolve_1.moduleKey; } });
|
|
66
|
+
Object.defineProperty(exports, "resolveProvisionModules", { enumerable: true, get: function () { return resolve_1.resolveProvisionModules; } });
|
|
56
67
|
var tool_schema_1 = require("./tool-schema");
|
|
57
68
|
Object.defineProperty(exports, "toolSchema", { enumerable: true, get: function () { return tool_schema_1.toolSchema; } });
|
|
58
69
|
var templates_2 = require("./tools/templates");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentic-kit/pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"author": "Dan Lynch <pyramation@gmail.com>",
|
|
5
5
|
"description": "Pi adapter for agentic-kit — the Constructive typed db tools (provision, blueprint, codegen, records, policies, templates) and confirm gate as a pi coding-agent extension",
|
|
6
6
|
"main": "index.js",
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
"test:watch": "jest --watch"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@agentic-kit/harness": "^0.
|
|
33
|
-
"@constructive-io/graphql-query": "^4.
|
|
34
|
-
"@constructive-io/sdk": "^1.
|
|
32
|
+
"@agentic-kit/harness": "^0.5.0",
|
|
33
|
+
"@constructive-io/graphql-query": "^4.8.0",
|
|
34
|
+
"@constructive-io/sdk": "^1.7.0",
|
|
35
|
+
"node-type-registry": "^1.7.0",
|
|
35
36
|
"zod": "^4.4.3"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
|
@@ -47,5 +48,5 @@
|
|
|
47
48
|
"coding-agent",
|
|
48
49
|
"constructive"
|
|
49
50
|
],
|
|
50
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "3ba26a9dfa342feff5a2216aafea962132d51621"
|
|
51
52
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ProvisionOverlay } from './resolve';
|
|
2
|
+
export declare const PROVISION_MANIFEST_FILE = "provision.json";
|
|
3
|
+
export type ProvisionManifest = ProvisionOverlay;
|
|
4
|
+
/** Validate an untrusted value as a provision manifest, throwing on bad shape. */
|
|
5
|
+
export declare function parseProvisionManifest(value: unknown): ProvisionManifest;
|
|
6
|
+
/**
|
|
7
|
+
* Load `<dir>/provision.json` as an overlay, or `null` when absent. A malformed
|
|
8
|
+
* file throws so a broken overlay is surfaced rather than silently ignored.
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadProvisionManifest(dir: string): Promise<ProvisionManifest | null>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PROVISION_MANIFEST_FILE = void 0;
|
|
7
|
+
exports.parseProvisionManifest = parseProvisionManifest;
|
|
8
|
+
exports.loadProvisionManifest = loadProvisionManifest;
|
|
9
|
+
// `provision.json` overlay manifest.
|
|
10
|
+
//
|
|
11
|
+
// A pure-data overlay, distributed the same way skills are: written into a
|
|
12
|
+
// config/app directory (locally, or materialized there from appstash / a pinned
|
|
13
|
+
// git ref) and read at provision time. It NEVER installs or executes a package
|
|
14
|
+
// — the base preset is a pinned build-time dependency; this file only selects a
|
|
15
|
+
// preset and layers add/remove on top.
|
|
16
|
+
const promises_1 = require("node:fs/promises");
|
|
17
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
const zod_1 = require("zod");
|
|
19
|
+
exports.PROVISION_MANIFEST_FILE = 'provision.json';
|
|
20
|
+
const ModuleSchema = zod_1.z.union([
|
|
21
|
+
zod_1.z.string(),
|
|
22
|
+
zod_1.z.tuple([zod_1.z.string(), zod_1.z.record(zod_1.z.string(), zod_1.z.unknown())]),
|
|
23
|
+
]);
|
|
24
|
+
const ProvisionManifestSchema = zod_1.z
|
|
25
|
+
.object({
|
|
26
|
+
preset: zod_1.z.string().optional(),
|
|
27
|
+
add: zod_1.z.array(ModuleSchema).optional(),
|
|
28
|
+
remove: zod_1.z.array(zod_1.z.string()).optional(),
|
|
29
|
+
})
|
|
30
|
+
.strict();
|
|
31
|
+
/** Validate an untrusted value as a provision manifest, throwing on bad shape. */
|
|
32
|
+
function parseProvisionManifest(value) {
|
|
33
|
+
return ProvisionManifestSchema.parse(value);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Load `<dir>/provision.json` as an overlay, or `null` when absent. A malformed
|
|
37
|
+
* file throws so a broken overlay is surfaced rather than silently ignored.
|
|
38
|
+
*/
|
|
39
|
+
async function loadProvisionManifest(dir) {
|
|
40
|
+
const file = node_path_1.default.join(dir, exports.PROVISION_MANIFEST_FILE);
|
|
41
|
+
let raw;
|
|
42
|
+
try {
|
|
43
|
+
raw = await (0, promises_1.readFile)(file, 'utf8');
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(raw);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
throw new Error(`Invalid ${exports.PROVISION_MANIFEST_FILE} at ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
54
|
+
}
|
|
55
|
+
return parseProvisionManifest(parsed);
|
|
56
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { allModulePresets, getModulePreset, type ModulePreset } from 'node-type-registry';
|
|
2
|
+
export type ProvisionModule = string | [string, Record<string, unknown>];
|
|
3
|
+
export declare const DEFAULT_PROVISION_PRESET: string;
|
|
4
|
+
export { allModulePresets, getModulePreset, type ModulePreset };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getModulePreset = exports.allModulePresets = exports.DEFAULT_PROVISION_PRESET = void 0;
|
|
4
|
+
// Provision module presets — the canonical source of truth.
|
|
5
|
+
//
|
|
6
|
+
// The module list is NEVER hand-copied here. It is imported from the pinned
|
|
7
|
+
// `node-type-registry` package (the same presets the backend and dashboard use),
|
|
8
|
+
// so bumping that dependency is how the base module set moves. Hosts customize
|
|
9
|
+
// on top with an ordered overlay (see `resolve.ts`) rather than editing a list.
|
|
10
|
+
const node_type_registry_1 = require("node-type-registry");
|
|
11
|
+
Object.defineProperty(exports, "allModulePresets", { enumerable: true, get: function () { return node_type_registry_1.allModulePresets; } });
|
|
12
|
+
Object.defineProperty(exports, "getModulePreset", { enumerable: true, get: function () { return node_type_registry_1.getModulePreset; } });
|
|
13
|
+
// Default base preset. `b2b:storage` = the full multi-tenant B2B shape plus file
|
|
14
|
+
// storage, matching Constructive's app shape. Crucially it already includes
|
|
15
|
+
// `config_secrets_module` — the dependency the old hand-copied list dropped,
|
|
16
|
+
// which made the backend reject provisioning with `REQUIRES (config_secrets_module:app)`.
|
|
17
|
+
exports.DEFAULT_PROVISION_PRESET = node_type_registry_1.PresetB2bStorage.name;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ProvisionModule } from './presets';
|
|
2
|
+
export interface ProvisionOverlay {
|
|
3
|
+
/**
|
|
4
|
+
* Base preset name to resolve from (e.g. 'b2b:storage', 'full'). The last
|
|
5
|
+
* layer that sets `preset` wins; if none set it, `DEFAULT_PROVISION_PRESET`
|
|
6
|
+
* is used.
|
|
7
|
+
*/
|
|
8
|
+
preset?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Modules to add. An entry whose key matches an existing module OVERRIDES it
|
|
11
|
+
* (e.g. re-declaring `storage_module` with app-specific options); a new key
|
|
12
|
+
* is appended.
|
|
13
|
+
*/
|
|
14
|
+
add?: ProvisionModule[];
|
|
15
|
+
/**
|
|
16
|
+
* Module names or `name:scope` keys to drop from the base. A bare name
|
|
17
|
+
* removes every scope of that module; `name:scope` removes just that one.
|
|
18
|
+
*/
|
|
19
|
+
remove?: string[];
|
|
20
|
+
}
|
|
21
|
+
/** Stable identity for a module entry: `name` or `name:scope` when scoped. */
|
|
22
|
+
export declare function moduleKey(m: ProvisionModule): string;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the effective module list from a pinned preset plus ordered overlays.
|
|
25
|
+
* Pure: no I/O, no host access. Later overlays win (last-write-wins by key).
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveProvisionModules(layers?: ProvisionOverlay[]): ProvisionModule[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.moduleKey = moduleKey;
|
|
4
|
+
exports.resolveProvisionModules = resolveProvisionModules;
|
|
5
|
+
// Ordered-overlay resolution for provision modules.
|
|
6
|
+
//
|
|
7
|
+
// Mirrors the skills overlay model (`@agentic-kit/harness` skills manifest): a
|
|
8
|
+
// pinned base (a named preset from `node-type-registry`) is layered under zero
|
|
9
|
+
// or more overlays, later layers winning by module key. An empty overlay list
|
|
10
|
+
// resolves to the base preset unchanged.
|
|
11
|
+
const presets_1 = require("./presets");
|
|
12
|
+
function moduleName(m) {
|
|
13
|
+
return Array.isArray(m) ? m[0] : m;
|
|
14
|
+
}
|
|
15
|
+
function moduleScope(m) {
|
|
16
|
+
if (!Array.isArray(m))
|
|
17
|
+
return undefined;
|
|
18
|
+
const scope = m[1]?.scope;
|
|
19
|
+
return typeof scope === 'string' ? scope : undefined;
|
|
20
|
+
}
|
|
21
|
+
/** Stable identity for a module entry: `name` or `name:scope` when scoped. */
|
|
22
|
+
function moduleKey(m) {
|
|
23
|
+
const scope = moduleScope(m);
|
|
24
|
+
return scope ? `${moduleName(m)}:${scope}` : moduleName(m);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the effective module list from a pinned preset plus ordered overlays.
|
|
28
|
+
* Pure: no I/O, no host access. Later overlays win (last-write-wins by key).
|
|
29
|
+
*/
|
|
30
|
+
function resolveProvisionModules(layers = []) {
|
|
31
|
+
const presetName = [...layers].reverse().find((l) => l.preset)?.preset ?? presets_1.DEFAULT_PROVISION_PRESET;
|
|
32
|
+
const preset = (0, presets_1.getModulePreset)(presetName);
|
|
33
|
+
if (!preset) {
|
|
34
|
+
const known = presets_1.allModulePresets.map((p) => p.name).join(', ');
|
|
35
|
+
throw new Error(`Unknown provision preset "${presetName}". Known presets: ${known}.`);
|
|
36
|
+
}
|
|
37
|
+
// Ordered map keyed by module identity preserves position while allowing
|
|
38
|
+
// overrides (same key) and appends (new key).
|
|
39
|
+
const resolved = new Map();
|
|
40
|
+
for (const m of preset.modules)
|
|
41
|
+
resolved.set(moduleKey(m), m);
|
|
42
|
+
for (const layer of layers) {
|
|
43
|
+
for (const target of layer.remove ?? []) {
|
|
44
|
+
for (const key of [...resolved.keys()]) {
|
|
45
|
+
if (key === target || key.split(':')[0] === target)
|
|
46
|
+
resolved.delete(key);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const m of layer.add ?? [])
|
|
50
|
+
resolved.set(moduleKey(m), m);
|
|
51
|
+
}
|
|
52
|
+
return [...resolved.values()];
|
|
53
|
+
}
|
|
@@ -15,8 +15,9 @@ const db_probe_1 = require("../db-probe");
|
|
|
15
15
|
const create_database_provision_1 = require("../provision-database/create-database-provision");
|
|
16
16
|
const credential_1 = require("../provision-database/credential");
|
|
17
17
|
const env_file_1 = require("../provision-database/env-file");
|
|
18
|
-
const
|
|
18
|
+
const manifest_1 = require("../provision-database/manifest");
|
|
19
19
|
const pg_fixups_1 = require("../provision-database/pg-fixups");
|
|
20
|
+
const resolve_1 = require("../provision-database/resolve");
|
|
20
21
|
const DEFAULT_API_ENDPOINT = 'http://api.localhost:3000/graphql';
|
|
21
22
|
const DEFAULT_MODULES_ENDPOINT = 'http://modules.localhost:3000/graphql';
|
|
22
23
|
// The per-DB endpoints are subdomain.<domain>; the provisioning domain is the api
|
|
@@ -192,6 +193,24 @@ exports.provisionDatabaseTool = {
|
|
|
192
193
|
ok: false,
|
|
193
194
|
out: err instanceof Error ? err.message : String(err),
|
|
194
195
|
}));
|
|
196
|
+
// Resolve the module set: pinned base preset from node-type-registry, then
|
|
197
|
+
// ordered overlays (host default < project-local provision.json). No list is
|
|
198
|
+
// hand-copied here; overlays are pure data. A bad preset/manifest fails the
|
|
199
|
+
// run rather than provisioning a wrong module set.
|
|
200
|
+
let modules;
|
|
201
|
+
try {
|
|
202
|
+
const layers = [];
|
|
203
|
+
const hostOverlay = await host.provisionOverlay?.();
|
|
204
|
+
if (hostOverlay)
|
|
205
|
+
layers.push(hostOverlay);
|
|
206
|
+
const projectOverlay = await (0, manifest_1.loadProvisionManifest)(ctx.cwd);
|
|
207
|
+
if (projectOverlay)
|
|
208
|
+
layers.push(projectOverlay);
|
|
209
|
+
modules = (0, resolve_1.resolveProvisionModules)(layers);
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
return fail(`Cannot resolve provision modules: ${err instanceof Error ? err.message : String(err)}`);
|
|
213
|
+
}
|
|
195
214
|
const physicalDb = process.env.CONSTRUCTIVE_DB || 'constructive';
|
|
196
215
|
// Provision on the MODULES endpoint: createDatabaseProvisionModule creates
|
|
197
216
|
// the database owned by ownerId, provisions the module set, and bootstraps
|
|
@@ -206,7 +225,7 @@ exports.provisionDatabaseTool = {
|
|
|
206
225
|
databaseName,
|
|
207
226
|
domain: provisionDomain(apiEndpoint),
|
|
208
227
|
ownerId,
|
|
209
|
-
modules
|
|
228
|
+
modules,
|
|
210
229
|
})));
|
|
211
230
|
}
|
|
212
231
|
catch (err) {
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_PROVISION_MODULES = [
|
|
2
|
-
'users_module',
|
|
3
|
-
'membership_types_module',
|
|
4
|
-
['permissions_module', { scope: 'app' }],
|
|
5
|
-
['permissions_module', { scope: 'org' }],
|
|
6
|
-
['limits_module', { scope: 'app' }],
|
|
7
|
-
['limits_module', { scope: 'org' }],
|
|
8
|
-
['levels_module', { scope: 'app' }],
|
|
9
|
-
['levels_module', { scope: 'org' }],
|
|
10
|
-
['memberships_module', { scope: 'app' }],
|
|
11
|
-
['memberships_module', { scope: 'org' }],
|
|
12
|
-
'sessions_module',
|
|
13
|
-
'user_state_module',
|
|
14
|
-
'user_credentials_module',
|
|
15
|
-
['internal_secrets_module', { scope: 'app' }],
|
|
16
|
-
'emails_module',
|
|
17
|
-
'rls_module',
|
|
18
|
-
'user_auth_module',
|
|
19
|
-
'session_secrets_module',
|
|
20
|
-
'rate_limits_module',
|
|
21
|
-
'connected_accounts_module',
|
|
22
|
-
['identity_providers_module', { scope: 'app' }],
|
|
23
|
-
'webauthn_credentials_module',
|
|
24
|
-
'webauthn_auth_module',
|
|
25
|
-
'phone_numbers_module',
|
|
26
|
-
['profiles_module', { scope: 'app' }],
|
|
27
|
-
['profiles_module', { scope: 'org' }],
|
|
28
|
-
['hierarchy_module', { scope: 'org' }],
|
|
29
|
-
['invites_module', { scope: 'app' }],
|
|
30
|
-
['invites_module', { scope: 'org' }],
|
|
31
|
-
// Mirrors constructive-client: api_name links storage to the same api-<slug>
|
|
32
|
-
// endpoint the generated SDK targets (without it uploads fail with "not
|
|
33
|
-
// enabled for this endpoint"), prefix keeps app_buckets/app_files names off
|
|
34
|
-
// user tables, and buckets seeds the public bucket row uploads need.
|
|
35
|
-
[
|
|
36
|
-
'storage_module',
|
|
37
|
-
{ scope: 'app', api_name: 'api', prefix: 'app', buckets: [{ key: 'public', is_public: true }] },
|
|
38
|
-
],
|
|
39
|
-
'devices_module',
|
|
40
|
-
];
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_PROVISION_MODULES = void 0;
|
|
4
|
-
exports.DEFAULT_PROVISION_MODULES = [
|
|
5
|
-
'users_module',
|
|
6
|
-
'membership_types_module',
|
|
7
|
-
['permissions_module', { scope: 'app' }],
|
|
8
|
-
['permissions_module', { scope: 'org' }],
|
|
9
|
-
['limits_module', { scope: 'app' }],
|
|
10
|
-
['limits_module', { scope: 'org' }],
|
|
11
|
-
['levels_module', { scope: 'app' }],
|
|
12
|
-
['levels_module', { scope: 'org' }],
|
|
13
|
-
['memberships_module', { scope: 'app' }],
|
|
14
|
-
['memberships_module', { scope: 'org' }],
|
|
15
|
-
'sessions_module',
|
|
16
|
-
'user_state_module',
|
|
17
|
-
'user_credentials_module',
|
|
18
|
-
['internal_secrets_module', { scope: 'app' }],
|
|
19
|
-
'emails_module',
|
|
20
|
-
'rls_module',
|
|
21
|
-
'user_auth_module',
|
|
22
|
-
'session_secrets_module',
|
|
23
|
-
'rate_limits_module',
|
|
24
|
-
'connected_accounts_module',
|
|
25
|
-
['identity_providers_module', { scope: 'app' }],
|
|
26
|
-
'webauthn_credentials_module',
|
|
27
|
-
'webauthn_auth_module',
|
|
28
|
-
'phone_numbers_module',
|
|
29
|
-
['profiles_module', { scope: 'app' }],
|
|
30
|
-
['profiles_module', { scope: 'org' }],
|
|
31
|
-
['hierarchy_module', { scope: 'org' }],
|
|
32
|
-
['invites_module', { scope: 'app' }],
|
|
33
|
-
['invites_module', { scope: 'org' }],
|
|
34
|
-
// Mirrors constructive-client: api_name links storage to the same api-<slug>
|
|
35
|
-
// endpoint the generated SDK targets (without it uploads fail with "not
|
|
36
|
-
// enabled for this endpoint"), prefix keeps app_buckets/app_files names off
|
|
37
|
-
// user tables, and buckets seeds the public bucket row uploads need.
|
|
38
|
-
[
|
|
39
|
-
'storage_module',
|
|
40
|
-
{ scope: 'app', api_name: 'api', prefix: 'app', buckets: [{ key: 'public', is_public: true }] },
|
|
41
|
-
],
|
|
42
|
-
'devices_module',
|
|
43
|
-
];
|