@agentic-kit/pi 0.17.1 → 0.18.1

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.
Files changed (136) hide show
  1. package/README.md +5 -7
  2. package/confirm-gate.d.ts +1 -2
  3. package/esm/confirm-gate.d.ts +1 -2
  4. package/esm/host.d.ts +5 -110
  5. package/esm/host.js +5 -18
  6. package/esm/index.d.ts +17 -9
  7. package/esm/index.js +20 -38
  8. package/esm/pi-tool.d.ts +16 -0
  9. package/esm/pi-tool.js +32 -0
  10. package/host.d.ts +5 -110
  11. package/host.js +10 -22
  12. package/index.d.ts +17 -9
  13. package/index.js +47 -62
  14. package/package.json +6 -10
  15. package/pi-tool.d.ts +16 -0
  16. package/pi-tool.js +36 -0
  17. package/app-workspace.d.ts +0 -16
  18. package/app-workspace.js +0 -145
  19. package/context.d.ts +0 -62
  20. package/context.js +0 -282
  21. package/db-probe.d.ts +0 -30
  22. package/db-probe.js +0 -50
  23. package/esm/app-workspace.d.ts +0 -16
  24. package/esm/app-workspace.js +0 -134
  25. package/esm/context.d.ts +0 -62
  26. package/esm/context.js +0 -270
  27. package/esm/db-probe.d.ts +0 -30
  28. package/esm/db-probe.js +0 -47
  29. package/esm/policy/add-policies-to-table.d.ts +0 -26
  30. package/esm/policy/add-policies-to-table.js +0 -66
  31. package/esm/policy/provision-helpers.d.ts +0 -38
  32. package/esm/policy/provision-helpers.js +0 -50
  33. package/esm/provision-database/credential.d.ts +0 -14
  34. package/esm/provision-database/credential.js +0 -19
  35. package/esm/provision-database/env-file.d.ts +0 -37
  36. package/esm/provision-database/env-file.js +0 -85
  37. package/esm/provision-database/manifest.d.ts +0 -10
  38. package/esm/provision-database/manifest.js +0 -48
  39. package/esm/provision-database/pg-fixups.d.ts +0 -16
  40. package/esm/provision-database/pg-fixups.js +0 -132
  41. package/esm/provision-database/preset-match.d.ts +0 -13
  42. package/esm/provision-database/preset-match.js +0 -66
  43. package/esm/provision-database/presets.d.ts +0 -4
  44. package/esm/provision-database/presets.js +0 -13
  45. package/esm/provision-database/request-database.d.ts +0 -61
  46. package/esm/provision-database/request-database.js +0 -130
  47. package/esm/provision-database/resolve.d.ts +0 -27
  48. package/esm/provision-database/resolve.js +0 -49
  49. package/esm/records/meta.d.ts +0 -41
  50. package/esm/records/meta.js +0 -167
  51. package/esm/run-codegen/barrels.d.ts +0 -3
  52. package/esm/run-codegen/barrels.js +0 -48
  53. package/esm/run-codegen/endpoints.d.ts +0 -8
  54. package/esm/run-codegen/endpoints.js +0 -46
  55. package/esm/schema-resolve.d.ts +0 -23
  56. package/esm/schema-resolve.js +0 -69
  57. package/esm/tool-schema.d.ts +0 -3
  58. package/esm/tool-schema.js +0 -10
  59. package/esm/tools/add-policies.d.ts +0 -8
  60. package/esm/tools/add-policies.js +0 -96
  61. package/esm/tools/add-records.d.ts +0 -14
  62. package/esm/tools/add-records.js +0 -124
  63. package/esm/tools/add-relation-schema.d.ts +0 -30
  64. package/esm/tools/add-relation-schema.js +0 -58
  65. package/esm/tools/add-relation.d.ts +0 -10
  66. package/esm/tools/add-relation.js +0 -91
  67. package/esm/tools/create-api-key.d.ts +0 -18
  68. package/esm/tools/create-api-key.js +0 -216
  69. package/esm/tools/describe-schema.d.ts +0 -18
  70. package/esm/tools/describe-schema.js +0 -132
  71. package/esm/tools/manage-entity-types.d.ts +0 -41
  72. package/esm/tools/manage-entity-types.js +0 -203
  73. package/esm/tools/mutations.d.ts +0 -14
  74. package/esm/tools/mutations.js +0 -235
  75. package/esm/tools/provision-blueprint.d.ts +0 -22
  76. package/esm/tools/provision-blueprint.js +0 -111
  77. package/esm/tools/provision-database.d.ts +0 -14
  78. package/esm/tools/provision-database.js +0 -262
  79. package/esm/tools/run-codegen.d.ts +0 -11
  80. package/esm/tools/run-codegen.js +0 -139
  81. package/esm/tools/templates.d.ts +0 -49
  82. package/esm/tools/templates.js +0 -337
  83. package/policy/add-policies-to-table.d.ts +0 -26
  84. package/policy/add-policies-to-table.js +0 -71
  85. package/policy/provision-helpers.d.ts +0 -38
  86. package/policy/provision-helpers.js +0 -58
  87. package/provision-database/credential.d.ts +0 -14
  88. package/provision-database/credential.js +0 -22
  89. package/provision-database/env-file.d.ts +0 -37
  90. package/provision-database/env-file.js +0 -91
  91. package/provision-database/manifest.d.ts +0 -10
  92. package/provision-database/manifest.js +0 -56
  93. package/provision-database/pg-fixups.d.ts +0 -16
  94. package/provision-database/pg-fixups.js +0 -170
  95. package/provision-database/preset-match.d.ts +0 -13
  96. package/provision-database/preset-match.js +0 -70
  97. package/provision-database/presets.d.ts +0 -4
  98. package/provision-database/presets.js +0 -17
  99. package/provision-database/request-database.d.ts +0 -61
  100. package/provision-database/request-database.js +0 -134
  101. package/provision-database/resolve.d.ts +0 -27
  102. package/provision-database/resolve.js +0 -53
  103. package/records/meta.d.ts +0 -41
  104. package/records/meta.js +0 -172
  105. package/run-codegen/barrels.d.ts +0 -3
  106. package/run-codegen/barrels.js +0 -54
  107. package/run-codegen/endpoints.d.ts +0 -8
  108. package/run-codegen/endpoints.js +0 -51
  109. package/schema-resolve.d.ts +0 -23
  110. package/schema-resolve.js +0 -75
  111. package/tool-schema.d.ts +0 -3
  112. package/tool-schema.js +0 -13
  113. package/tools/add-policies.d.ts +0 -8
  114. package/tools/add-policies.js +0 -99
  115. package/tools/add-records.d.ts +0 -14
  116. package/tools/add-records.js +0 -127
  117. package/tools/add-relation-schema.d.ts +0 -30
  118. package/tools/add-relation-schema.js +0 -62
  119. package/tools/add-relation.d.ts +0 -10
  120. package/tools/add-relation.js +0 -94
  121. package/tools/create-api-key.d.ts +0 -18
  122. package/tools/create-api-key.js +0 -222
  123. package/tools/describe-schema.d.ts +0 -18
  124. package/tools/describe-schema.js +0 -135
  125. package/tools/manage-entity-types.d.ts +0 -41
  126. package/tools/manage-entity-types.js +0 -208
  127. package/tools/mutations.d.ts +0 -14
  128. package/tools/mutations.js +0 -238
  129. package/tools/provision-blueprint.d.ts +0 -22
  130. package/tools/provision-blueprint.js +0 -114
  131. package/tools/provision-database.d.ts +0 -14
  132. package/tools/provision-database.js +0 -268
  133. package/tools/run-codegen.d.ts +0 -11
  134. package/tools/run-codegen.js +0 -143
  135. package/tools/templates.d.ts +0 -49
  136. package/tools/templates.js +0 -341
@@ -1,41 +0,0 @@
1
- import type { Table, TableInflection } from '@constructive-io/graphql-query/types/schema';
2
- export declare const META_QUERY = "\n query DbToolsMeta {\n _meta {\n tables {\n name\n query { all one create update delete }\n inflection {\n allRows\n conditionType\n connection\n createInputType\n createPayloadType\n deletePayloadType\n edge\n filterType\n orderByType\n patchType\n tableType\n updatePayloadType\n }\n fields {\n name\n isNotNull\n hasDefault\n type {\n gqlType\n isArray\n pgType\n subtype\n isNotNull\n hasDefault\n }\n }\n }\n }\n }\n";
3
- type RawType = {
4
- gqlType: string;
5
- isArray: boolean;
6
- modifier?: string | number | null;
7
- pgAlias?: string | null;
8
- pgType?: string | null;
9
- subtype?: string | null;
10
- typmod?: number | null;
11
- isNotNull?: boolean | null;
12
- hasDefault?: boolean | null;
13
- };
14
- type RawField = {
15
- name: string;
16
- isNotNull?: boolean | null;
17
- hasDefault?: boolean | null;
18
- type: RawType;
19
- };
20
- type RawInflection = Partial<Record<keyof TableInflection, string | null>>;
21
- type RawQuery = {
22
- all: string | null;
23
- one?: string | null;
24
- create?: string | null;
25
- update?: string | null;
26
- delete?: string | null;
27
- };
28
- export type RawMetaTable = {
29
- name: string;
30
- query?: RawQuery | null;
31
- inflection?: RawInflection | null;
32
- fields?: (RawField | null)[] | null;
33
- };
34
- export type MetaResponse = {
35
- _meta?: {
36
- tables?: (RawMetaTable | null)[] | null;
37
- } | null;
38
- };
39
- export declare function cleanTable(meta: RawMetaTable): Table;
40
- export declare function findMetaTable(tables: RawMetaTable[], requested: string): RawMetaTable | undefined;
41
- export {};
@@ -1,167 +0,0 @@
1
- // Vendored from constructive-client's `@constructive-io/data` (not published to
2
- // the registry). We query the per-DB data endpoint's `_meta` introspection and
3
- // reshape each table into the `Table` shape `buildPostGraphileCreate` expects.
4
- // Relations are intentionally left empty: the create-mutation builder only reads
5
- // them to skip relational fields in the return selection, and every `_meta` field
6
- // is a real scalar column, so selecting them all is valid.
7
- //
8
- // Only fields the live backend's `_meta` actually exposes are selected here —
9
- // over-selecting (e.g. MetaType.modifier or MetaInflection.createField) fails
10
- // GraphQL validation. The reshapers below tolerate the omitted fields.
11
- export const META_QUERY = `
12
- query DbToolsMeta {
13
- _meta {
14
- tables {
15
- name
16
- query { all one create update delete }
17
- inflection {
18
- allRows
19
- conditionType
20
- connection
21
- createInputType
22
- createPayloadType
23
- deletePayloadType
24
- edge
25
- filterType
26
- orderByType
27
- patchType
28
- tableType
29
- updatePayloadType
30
- }
31
- fields {
32
- name
33
- isNotNull
34
- hasDefault
35
- type {
36
- gqlType
37
- isArray
38
- pgType
39
- subtype
40
- isNotNull
41
- hasDefault
42
- }
43
- }
44
- }
45
- }
46
- }
47
- `;
48
- function convertInflection(inflection) {
49
- if (!inflection)
50
- return undefined;
51
- const s = (key) => inflection[key] ?? '';
52
- const n = (key) => inflection[key] ?? null;
53
- return {
54
- allRows: s('allRows'),
55
- allRowsSimple: s('allRowsSimple'),
56
- conditionType: s('conditionType'),
57
- connection: s('connection'),
58
- createField: s('createField'),
59
- createInputType: s('createInputType'),
60
- createPayloadType: s('createPayloadType'),
61
- deleteByPrimaryKey: n('deleteByPrimaryKey'),
62
- deletePayloadType: s('deletePayloadType'),
63
- edge: s('edge'),
64
- edgeField: s('edgeField'),
65
- enumType: s('enumType'),
66
- filterType: n('filterType'),
67
- inputType: s('inputType'),
68
- orderByType: s('orderByType'),
69
- patchField: s('patchField'),
70
- patchType: n('patchType'),
71
- tableFieldName: s('tableFieldName'),
72
- tableType: s('tableType'),
73
- typeName: s('typeName'),
74
- updateByPrimaryKey: n('updateByPrimaryKey'),
75
- updatePayloadType: n('updatePayloadType'),
76
- };
77
- }
78
- function convertQuery(query) {
79
- if (!query)
80
- return undefined;
81
- return {
82
- all: query.all ?? '',
83
- one: query.one ?? null,
84
- create: query.create ?? '',
85
- update: query.update ?? null,
86
- delete: query.delete ?? null,
87
- };
88
- }
89
- export function cleanTable(meta) {
90
- return {
91
- name: meta.name,
92
- inflection: convertInflection(meta.inflection),
93
- query: convertQuery(meta.query),
94
- fields: (meta.fields ?? [])
95
- .filter((f) => Boolean(f))
96
- .map((field) => ({
97
- name: field.name,
98
- type: {
99
- gqlType: field.type.gqlType,
100
- isArray: field.type.isArray,
101
- modifier: field.type.modifier,
102
- pgAlias: field.type.pgAlias,
103
- pgType: field.type.pgType,
104
- subtype: field.type.subtype,
105
- typmod: field.type.typmod,
106
- },
107
- isNotNull: field.isNotNull ?? field.type.isNotNull ?? null,
108
- hasDefault: field.hasDefault ?? field.type.hasDefault ?? null,
109
- })),
110
- relations: { belongsTo: [], hasOne: [], hasMany: [], manyToMany: [] },
111
- };
112
- }
113
- function normalizeTableLookupKey(value) {
114
- return (value ?? '')
115
- .trim()
116
- .toLowerCase()
117
- .replace(/[^a-z0-9]/g, '');
118
- }
119
- function getTableLookupVariants(value) {
120
- const normalized = normalizeTableLookupKey(value);
121
- if (!normalized)
122
- return [];
123
- const variants = new Set([normalized]);
124
- if (normalized.endsWith('y')) {
125
- variants.add(`${normalized}s`);
126
- variants.add(`${normalized.slice(0, -1)}ies`);
127
- }
128
- if (normalized.endsWith('ies')) {
129
- variants.add(`${normalized.slice(0, -3)}y`);
130
- }
131
- if (/(s|x|z|ch|sh)$/.test(normalized))
132
- variants.add(`${normalized}es`);
133
- if (normalized.endsWith('es'))
134
- variants.add(normalized.slice(0, -2));
135
- if (!normalized.endsWith('s'))
136
- variants.add(`${normalized}s`);
137
- if (normalized.endsWith('s'))
138
- variants.add(normalized.slice(0, -1));
139
- return [...variants].filter(Boolean);
140
- }
141
- // Resolve a user-supplied table name (snake_case singular or plural) against the
142
- // PascalCase-singular `_meta` table names, tolerating pluralization differences.
143
- export function findMetaTable(tables, requested) {
144
- const index = new Map();
145
- for (const table of tables) {
146
- const keys = [
147
- table.name,
148
- table.query?.all,
149
- table.query?.one,
150
- table.inflection?.tableType,
151
- table.inflection?.tableFieldName,
152
- table.inflection?.allRows,
153
- ];
154
- for (const key of keys) {
155
- for (const variant of getTableLookupVariants(key)) {
156
- if (!index.has(variant))
157
- index.set(variant, table);
158
- }
159
- }
160
- }
161
- for (const variant of getTableLookupVariants(requested)) {
162
- const match = index.get(variant);
163
- if (match)
164
- return match;
165
- }
166
- return undefined;
167
- }
@@ -1,3 +0,0 @@
1
- /** Rewrite directory barrel specifiers under `root` in place. Returns the count
2
- * of files changed. No-op (returns 0) when `root` does not exist. */
3
- export declare function normalizeSdkBarrels(root: string): number;
@@ -1,48 +0,0 @@
1
- // Turbopack barrel normalization (TURBOPACK-BARREL-001).
2
- //
3
- // Codegen emits directory barrel re-exports like `export * from './hooks'` where
4
- // `./hooks` is a directory with an index.ts. Next.js 16's Turbopack cannot
5
- // resolve directory specifiers — it needs an explicit `./hooks/index`. This pass
6
- // rewrites `./X` -> `./X/index` only when X resolves to a sibling DIRECTORY,
7
- // leaving file specifiers (and ones already ending in /index) untouched.
8
- //
9
- // Pure-ish: operates on a real directory tree (so it's exercised with a temp
10
- // fixture in tests) and is idempotent — safe to re-run.
11
- import fs from 'node:fs';
12
- import path from 'node:path';
13
- const SPEC_RE = /((?:export|import)[^\n]*?from\s*["'])(\.\.?\/[^"']+?)(["'])/g;
14
- const FILE_EXT_RE = /\.(tsx?|jsx?|json|css)$/;
15
- /** Rewrite directory barrel specifiers under `root` in place. Returns the count
16
- * of files changed. No-op (returns 0) when `root` does not exist. */
17
- export function normalizeSdkBarrels(root) {
18
- if (!fs.existsSync(root))
19
- return 0;
20
- let changed = 0;
21
- const walk = (dir) => {
22
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
23
- const p = path.join(dir, entry.name);
24
- if (entry.isDirectory()) {
25
- walk(p);
26
- continue;
27
- }
28
- if (!/\.tsx?$/.test(entry.name))
29
- continue;
30
- const orig = fs.readFileSync(p, 'utf8');
31
- const next = orig.replace(SPEC_RE, (match, pre, spec, post) => {
32
- if (FILE_EXT_RE.test(spec))
33
- return match; // already a file
34
- const abs = path.resolve(path.dirname(p), spec);
35
- if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) {
36
- return pre + spec.replace(/\/$/, '') + '/index' + post;
37
- }
38
- return match;
39
- });
40
- if (next !== orig) {
41
- fs.writeFileSync(p, next);
42
- changed++;
43
- }
44
- }
45
- };
46
- walk(root);
47
- return changed;
48
- }
@@ -1,8 +0,0 @@
1
- export type PlaneEndpoints = {
2
- admin: string;
3
- auth: string;
4
- app: string;
5
- };
6
- export declare function derivePlaneEndpoints(apiEndpoint: string, databaseName: string): PlaneEndpoints | undefined;
7
- export declare function codegenEndpointEnv(planes: PlaneEndpoints): Record<string, string>;
8
- export declare function envLocalContent(databaseName: string, planes: PlaneEndpoints | undefined): string;
@@ -1,46 +0,0 @@
1
- // Codegen and the Next.js dev server default every per-DB vhost to
2
- // *-<db>.localhost:3000, which only exists on a local backend. Derive the real
3
- // vhosts from the api endpoint the binding belongs to by swapping its first
4
- // host label (api.launchql.dev -> admin-<db>.launchql.dev) — the same scheme
5
- // context.ts uses for the data plane. The app DATA plane is `api-<db>`, not
6
- // `app-<db>` (SUBDOMAIN-001).
7
- export function derivePlaneEndpoints(apiEndpoint, databaseName) {
8
- const derive = (label) => {
9
- try {
10
- const url = new URL(apiEndpoint);
11
- const labels = url.hostname.split('.');
12
- labels[0] = `${label}-${databaseName}`;
13
- url.hostname = labels.join('.');
14
- return url.toString();
15
- }
16
- catch {
17
- return undefined;
18
- }
19
- };
20
- const admin = derive('admin');
21
- const auth = derive('auth');
22
- const app = derive('api');
23
- return admin && auth && app ? { admin, auth, app } : undefined;
24
- }
25
- // The template's graphql-codegen.config.ts honors CODEGEN_*_ENDPOINT for the
26
- // URL and CODEGEN_*_HOST for the Host header; set both so scheme and routing
27
- // stay in sync on any backend.
28
- export function codegenEndpointEnv(planes) {
29
- return {
30
- CODEGEN_ADMIN_ENDPOINT: planes.admin,
31
- CODEGEN_ADMIN_HOST: new URL(planes.admin).host,
32
- CODEGEN_AUTH_ENDPOINT: planes.auth,
33
- CODEGEN_AUTH_HOST: new URL(planes.auth).host,
34
- CODEGEN_APP_ENDPOINT: planes.app,
35
- CODEGEN_APP_HOST: new URL(planes.app).host,
36
- };
37
- }
38
- // .env.local for the dev server: the template's runtime config falls back to
39
- // localhost vhosts unless NEXT_PUBLIC_*_ENDPOINT overrides are present.
40
- export function envLocalContent(databaseName, planes) {
41
- const lines = [`NEXT_PUBLIC_DB_NAME=${databaseName}`];
42
- if (planes) {
43
- lines.push(`NEXT_PUBLIC_ADMIN_ENDPOINT=${planes.admin}`, `NEXT_PUBLIC_AUTH_ENDPOINT=${planes.auth}`, `NEXT_PUBLIC_APP_ENDPOINT=${planes.app}`);
44
- }
45
- return `${lines.join('\n')}\n`;
46
- }
@@ -1,23 +0,0 @@
1
- import type { ProjectContext } from './context';
2
- export type ResolvedField = {
3
- id: string;
4
- name: string;
5
- tableId: string;
6
- };
7
- export type ResolvedUniqueConstraint = {
8
- id: string;
9
- fieldIds: string[];
10
- };
11
- export type ResolvedTable = {
12
- id: string;
13
- name: string;
14
- fields: ResolvedField[];
15
- uniqueConstraints: ResolvedUniqueConstraint[];
16
- };
17
- export type ResolvedSchema = {
18
- tables: ResolvedTable[];
19
- };
20
- export declare function resolveSchema(context: ProjectContext): Promise<ResolvedSchema>;
21
- export declare function resolveTable(schema: ResolvedSchema, tableName: string): ResolvedTable;
22
- export declare function resolveField(table: ResolvedTable, fieldName: string): ResolvedField;
23
- export declare function findUniqueConstraintId(table: ResolvedTable, fieldId: string): string | undefined;
@@ -1,69 +0,0 @@
1
- export async function resolveSchema(context) {
2
- const { api, databaseId } = context;
3
- const tablesResult = await api.table
4
- .findMany({
5
- select: { id: true, name: true },
6
- where: { databaseId: { equalTo: databaseId } },
7
- })
8
- .execute();
9
- if (!tablesResult.ok) {
10
- const message = tablesResult.errors.map((e) => e.message).join('; ');
11
- throw new Error(`Failed to read tables: ${message}`);
12
- }
13
- const tableNodes = tablesResult.data.tables?.nodes ?? [];
14
- const tableIds = tableNodes.map((t) => t.id);
15
- const fieldsByTable = new Map();
16
- const constraintsByTable = new Map();
17
- if (tableIds.length > 0) {
18
- const fieldsResult = await api.field
19
- .findMany({
20
- select: { id: true, name: true, tableId: true },
21
- where: { tableId: { in: tableIds } },
22
- })
23
- .execute();
24
- if (!fieldsResult.ok) {
25
- const message = fieldsResult.errors.map((e) => e.message).join('; ');
26
- throw new Error(`Failed to read fields: ${message}`);
27
- }
28
- for (const field of fieldsResult.data.fields?.nodes ?? []) {
29
- const list = fieldsByTable.get(field.tableId) ?? [];
30
- list.push({ id: field.id, name: field.name, tableId: field.tableId });
31
- fieldsByTable.set(field.tableId, list);
32
- }
33
- const constraintsResult = await api.uniqueConstraint
34
- .findMany({
35
- select: { id: true, tableId: true, fieldIds: true },
36
- where: { tableId: { in: tableIds } },
37
- })
38
- .execute();
39
- if (constraintsResult.ok) {
40
- for (const c of constraintsResult.data.uniqueConstraints?.nodes ?? []) {
41
- const list = constraintsByTable.get(c.tableId) ?? [];
42
- list.push({ id: c.id, fieldIds: Array.isArray(c.fieldIds) ? c.fieldIds : [] });
43
- constraintsByTable.set(c.tableId, list);
44
- }
45
- }
46
- }
47
- const tables = tableNodes.map((table) => ({
48
- id: table.id,
49
- name: table.name,
50
- fields: fieldsByTable.get(table.id) ?? [],
51
- uniqueConstraints: constraintsByTable.get(table.id) ?? [],
52
- }));
53
- return { tables };
54
- }
55
- export function resolveTable(schema, tableName) {
56
- const table = schema.tables.find((t) => t.name === tableName);
57
- if (!table)
58
- throw new Error(`Table "${tableName}" not found in current schema`);
59
- return table;
60
- }
61
- export function resolveField(table, fieldName) {
62
- const field = table.fields.find((f) => f.name === fieldName);
63
- if (!field)
64
- throw new Error(`Field "${fieldName}" not found in table "${table.name}"`);
65
- return field;
66
- }
67
- export function findUniqueConstraintId(table, fieldId) {
68
- return table.uniqueConstraints.find((c) => c.fieldIds.includes(fieldId))?.id;
69
- }
@@ -1,3 +0,0 @@
1
- import type { TSchema } from 'typebox';
2
- import { z } from 'zod';
3
- export declare function toolSchema(schema: z.ZodType): TSchema;
@@ -1,10 +0,0 @@
1
- import { z } from 'zod';
2
- // Tool parameter schemas are authored in zod; pi's ToolDefinition wants a
3
- // typebox TSchema. Structurally a TSchema is just a JSON Schema object, so the
4
- // draft-7 output of z.toJSONSchema (minus the $schema marker, matching what
5
- // typebox emits) is cast across the boundary. typebox itself is a type-only
6
- // import — it never appears in the runtime bundle.
7
- export function toolSchema(schema) {
8
- const { $schema: _$schema, ...rest } = z.toJSONSchema(schema, { target: 'draft-7' });
9
- return rest;
10
- }
@@ -1,8 +0,0 @@
1
- import type { ToolDefinition } from '@earendil-works/pi-coding-agent';
2
- declare const AddPoliciesSchema: import("typebox/type").TSchema;
3
- export type AddPoliciesDetails = {
4
- success: boolean;
5
- message: string;
6
- };
7
- export declare const addPoliciesTool: ToolDefinition<typeof AddPoliciesSchema, AddPoliciesDetails>;
8
- export {};
@@ -1,96 +0,0 @@
1
- import { buildNodeData, getPolicyFieldDefaults, getPolicyProvisioningConfig } from '@agentic-kit/harness';
2
- import { z } from 'zod';
3
- import { resolveProjectContext } from '../context';
4
- import { addPoliciesToExistingTable } from '../policy/add-policies-to-table';
5
- import { resolveSchema, resolveTable } from '../schema-resolve';
6
- import { toolSchema } from '../tool-schema';
7
- const DEFAULT_ROLE = 'authenticated';
8
- const CrudOperationSchema = z.enum(['create', 'read', 'update', 'delete']);
9
- const PolicyEntrySchema = z.object({
10
- policy_type: z
11
- .string()
12
- .describe('Policy type id. Supported: AuthzAllowAll, AuthzDenyAll, AuthzAppMembership, AuthzDirectOwner, AuthzEntityMembership, AuthzOrgHierarchy, AuthzPublishable (these auto-create columns via their Data* node), AuthzRelatedMemberList, AuthzRelatedEntityMembership. Not supported (use the policies UI): AuthzDirectOwnerAny, AuthzMemberList, AuthzTemporal, AuthzComposite.'),
13
- operations: z
14
- .array(CrudOperationSchema)
15
- .min(1)
16
- .describe('CRUD operations the policy applies to: any subset of ["create","read","update","delete"].'),
17
- role_name: z
18
- .string()
19
- .describe('PostgreSQL role the policy applies to. Defaults to "authenticated".')
20
- .optional(),
21
- is_permissive: z
22
- .boolean()
23
- .describe('PERMISSIVE (default true) vs RESTRICTIVE — most policies should be permissive.')
24
- .optional(),
25
- data: z
26
- .record(z.string(), z.unknown())
27
- .describe('Type-specific shared data. AuthzDirectOwner: { entity_field: "owner_id" }. AuthzAppMembership: { permission?, is_admin?, is_owner? }. Empty object {} for AuthzAllowAll / AuthzDenyAll.')
28
- .optional(),
29
- node_data: z
30
- .record(z.string(), z.unknown())
31
- .describe('Optional data for the underlying Data* node (has-module policies only).')
32
- .optional(),
33
- });
34
- const AddPoliciesZod = z.object({
35
- table_name: z
36
- .string()
37
- .describe('Name of the existing table to attach the policies to. Use describe_schema first to verify it exists.'),
38
- policies: z
39
- .array(PolicyEntrySchema)
40
- .min(1)
41
- .describe('One or more policies to add. Each entry becomes one secure_table_provision call.'),
42
- });
43
- const AddPoliciesSchema = toolSchema(AddPoliciesZod);
44
- function buildOperations(roleName, isPermissive) {
45
- const config = { roleName, isPermissive, policyData: {}, isCustomized: false };
46
- return { create: config, read: config, update: config, delete: config };
47
- }
48
- export const addPoliciesTool = {
49
- name: 'add_policies',
50
- label: 'Add policies',
51
- description: 'Add one or more RLS policies to an existing table. Has-module policies (AuthzDirectOwner, AuthzEntityMembership, AuthzOrgHierarchy, AuthzPublishable) auto-create the columns they need — call directly, do not add columns first.',
52
- promptSnippet: 'add_policies: attach RLS policies to an existing table (one call can add many). Gated.',
53
- parameters: AddPoliciesSchema,
54
- async execute(_id, params, _signal, _onUpdate, ctx) {
55
- const resolved = await resolveProjectContext(ctx.cwd);
56
- if (!resolved.context) {
57
- return {
58
- content: [{ type: 'text', text: resolved.reason }],
59
- details: { success: false, message: resolved.reason },
60
- };
61
- }
62
- const { modules, databaseId, schemaId } = resolved.context;
63
- try {
64
- const schema = await resolveSchema(resolved.context);
65
- const table = resolveTable(schema, params.table_name);
66
- const policies = params.policies.map((entry) => {
67
- const config = getPolicyProvisioningConfig(entry.policy_type);
68
- const defaults = getPolicyFieldDefaults(entry.policy_type);
69
- const mergedData = { ...defaults, ...(entry.data ?? {}) };
70
- const nodeData = entry.node_data ?? buildNodeData(entry.policy_type, mergedData);
71
- return {
72
- policyType: entry.policy_type,
73
- dataNodeType: config?.dataNodeType,
74
- nodeData,
75
- sharedPolicyData: mergedData,
76
- operations: buildOperations(entry.role_name ?? DEFAULT_ROLE, entry.is_permissive ?? true),
77
- enabledOperations: entry.operations,
78
- };
79
- });
80
- await addPoliciesToExistingTable({
81
- client: modules,
82
- databaseId,
83
- schemaId,
84
- tableId: table.id,
85
- policies,
86
- });
87
- const count = params.policies.length;
88
- const message = `Added ${count} ${count === 1 ? 'policy' : 'policies'} to ${params.table_name}`;
89
- return { content: [{ type: 'text', text: message }], details: { success: true, message } };
90
- }
91
- catch (err) {
92
- const message = err instanceof Error ? err.message : 'Failed to add policies';
93
- return { content: [{ type: 'text', text: message }], details: { success: false, message } };
94
- }
95
- },
96
- };
@@ -1,14 +0,0 @@
1
- import type { ToolDefinition } from '@earendil-works/pi-coding-agent';
2
- export type AddRecordsDetails = {
3
- success: boolean;
4
- message: string;
5
- tableName?: string;
6
- columns?: string[];
7
- rows?: Record<string, unknown>[];
8
- createdCount?: number;
9
- asOf?: string;
10
- needsAuth?: boolean;
11
- };
12
- declare const AddRecordsSchema: import("typebox/type").TSchema;
13
- export declare const addRecordsTool: ToolDefinition<typeof AddRecordsSchema, AddRecordsDetails>;
14
- export {};
@@ -1,124 +0,0 @@
1
- import { buildPostGraphileCreate, toCamelCaseSingular, toCreateMutationName, } from '@constructive-io/graphql-query/generators';
2
- import { objects } from '@constructive-io/sdk';
3
- import { z } from 'zod';
4
- import { resolveDataToken, resolveProjectContext } from '../context';
5
- import { cleanTable, findMetaTable, META_QUERY } from '../records/meta';
6
- import { toolSchema } from '../tool-schema';
7
- function fail(message) {
8
- return { content: [{ type: 'text', text: message }], details: { success: false, message } };
9
- }
10
- function needsAuth(message) {
11
- return {
12
- content: [{ type: 'text', text: message }],
13
- details: { success: false, message, needsAuth: true },
14
- };
15
- }
16
- // Mirror @constructive-io/data's prepareCreateInput (create semantics): drop
17
- // undefined/null/empty-string so server defaults apply; keep false/0/[]/objects.
18
- function stripForCreate(record) {
19
- const out = {};
20
- for (const [key, value] of Object.entries(record)) {
21
- if (value === undefined || value === null || value === '')
22
- continue;
23
- out[key] = value;
24
- }
25
- return out;
26
- }
27
- const AddRecordsZod = z.object({
28
- table_name: z
29
- .string()
30
- .describe('Name of the table to insert rows into (snake_case, singular or plural).'),
31
- records: z
32
- .array(z.record(z.string(), z.unknown()))
33
- .describe('Rows to insert. Each is an object of column → value (camelCase or snake_case column names).'),
34
- });
35
- const AddRecordsSchema = toolSchema(AddRecordsZod);
36
- export const addRecordsTool = {
37
- name: 'add_records',
38
- label: 'Add records',
39
- description: "Insert one or more rows into a table in the project's app database.",
40
- promptSnippet: 'add_records: insert rows into an existing table. Gated.',
41
- parameters: AddRecordsSchema,
42
- async execute(_id, params, _signal, _onUpdate, ctx) {
43
- const resolved = await resolveProjectContext(ctx.cwd);
44
- if (!resolved.context)
45
- return fail(resolved.reason);
46
- const { dataEndpoint } = resolved.context;
47
- if (!dataEndpoint) {
48
- return fail('Cannot resolve the app data endpoint (DATABASE_NAME missing from .env). Re-provision the database, then retry.');
49
- }
50
- if (params.records.length === 0)
51
- return fail('No records provided.');
52
- const token = await resolveDataToken(resolved.context);
53
- if (!token.token)
54
- return needsAuth(token.reason ?? 'Sign in to the database to insert records.');
55
- const adapter = new objects.FetchAdapter(dataEndpoint, {
56
- Authorization: `Bearer ${token.token}`,
57
- });
58
- let tables;
59
- try {
60
- const meta = await adapter.execute(META_QUERY);
61
- if (!meta.ok) {
62
- return fail(`Failed to read schema from the app database: ${meta.errors.map((e) => e.message).join('; ')}`);
63
- }
64
- tables = (meta.data._meta?.tables ?? []).filter((t) => Boolean(t));
65
- }
66
- catch (err) {
67
- return fail(err instanceof Error ? err.message : 'Failed to query the app database schema.');
68
- }
69
- const rawTable = findMetaTable(tables, params.table_name);
70
- if (!rawTable) {
71
- return fail(`Table "${params.table_name}" was not found in the app database.`);
72
- }
73
- const table = cleanTable(rawTable);
74
- const allTables = tables.map(cleanTable);
75
- const mutation = buildPostGraphileCreate(table, allTables).toString();
76
- const mutationName = toCreateMutationName(table.name, table);
77
- const singularName = toCamelCaseSingular(table.name, table);
78
- // Constructive app tables scope rows by entityId (the owning user's id). The
79
- // create input requires it, but callers won't know it — backfill it with the
80
- // signed-in user's id from the Preview session when the table has the column
81
- // and the record omits it.
82
- const hasEntityId = table.fields.some((f) => f.name === 'entityId');
83
- const created = [];
84
- const errors = [];
85
- for (const record of params.records) {
86
- const prepared = hasEntityId && token.userId && record.entityId == null && record.entity_id == null
87
- ? { ...record, entityId: token.userId }
88
- : record;
89
- const variables = { input: { [singularName]: stripForCreate(prepared) } };
90
- try {
91
- const result = await adapter.execute(mutation, variables);
92
- if (!result.ok) {
93
- errors.push(result.errors.map((e) => e.message).join('; '));
94
- continue;
95
- }
96
- const payload = result.data[mutationName];
97
- const row = payload?.[singularName] ?? {};
98
- created.push(row);
99
- }
100
- catch (err) {
101
- errors.push(err instanceof Error ? err.message : 'Unknown error');
102
- }
103
- }
104
- if (created.length === 0) {
105
- return fail(`Failed to insert into "${params.table_name}": ${errors.join('; ') || 'unknown error'}`);
106
- }
107
- const columns = table.fields.map((f) => f.name);
108
- const message = errors.length > 0
109
- ? `Inserted ${created.length} of ${params.records.length} row(s) into ${params.table_name} (${errors.length} failed).`
110
- : `Inserted ${created.length} row(s) into ${params.table_name}.`;
111
- return {
112
- content: [{ type: 'text', text: message }],
113
- details: {
114
- success: true,
115
- message,
116
- tableName: rawTable.name,
117
- columns,
118
- rows: created,
119
- createdCount: created.length,
120
- asOf: new Date().toISOString(),
121
- },
122
- };
123
- },
124
- };