@agentic-kit/pi 0.13.0 → 0.14.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/README.md CHANGED
@@ -38,6 +38,36 @@ const dbTools = createDbTools({
38
38
  pi.use(dbTools);
39
39
  ```
40
40
 
41
+ ## Project context
42
+
43
+ The tools need a bound database: an access key, a database id, and optionally endpoint/name pins. Where those values come from is the host's choice — `resolveProjectContext` takes values, not a directory:
44
+
45
+ ```ts
46
+ import { fromEnvironment, resolveProjectContext } from '@agentic-kit/pi';
47
+
48
+ // headless: container, Job, CI — nothing on disk, nothing to commit
49
+ await resolveProjectContext(fromEnvironment());
50
+
51
+ // local project: read <cwd>/.env (what Desktop and the CLI do)
52
+ await resolveProjectContext(cwd);
53
+
54
+ // anything else: a record, or a lookup function into a secret store
55
+ await resolveProjectContext((name) => vault.get(name));
56
+ ```
57
+
58
+ Injected variables carry a `CONSTRUCTIVE_` prefix; the bare names are also accepted (that is what the scaffolder writes into a project `.env`), with the prefixed spelling winning:
59
+
60
+ | Variable | Required | Purpose |
61
+ | --- | --- | --- |
62
+ | `CONSTRUCTIVE_ACCESS_TOKEN` | yes | project data-plane key |
63
+ | `CONSTRUCTIVE_DATABASE_ID` | yes | bound database |
64
+ | `CONSTRUCTIVE_DATABASE_NAME` | no | derives the per-db data endpoint |
65
+ | `CONSTRUCTIVE_API_ENDPOINT` | no | data-plane api pin |
66
+ | `CONSTRUCTIVE_MODULES_ENDPOINT` | no | data-plane modules pin |
67
+ | `CONSTRUCTIVE_OWNER_ID` | no | owner fallback when the probe omits it |
68
+
69
+ Endpoint pins from the source apply to the **data plane only**. The control plane (binding probe, schema resolution, blueprint/schema tools) always uses the host's `backendConfig()` and the account bearer, so an untrusted cloned project cannot redirect it.
70
+
41
71
  ## Provisioning
42
72
 
43
73
  `provision_database` requests a database through the `requestDatabase` mutation on the api endpoint. When the requested module set matches a cataloged preset, the backend claims a warm pre-baked database in seconds. Otherwise a background job provisions the database cold. The tool polls the provision ticket on the modules endpoint until the database and its owner bootstrap are complete. Then it writes the credentials to the project `.env` and returns.
package/context.d.ts CHANGED
@@ -19,6 +19,21 @@ export type ResolveResult = {
19
19
  reason: string;
20
20
  code?: ProjectContextFailureCode;
21
21
  };
22
+ export declare const CONTEXT_ENV_KEYS: readonly ["ACCESS_TOKEN", "DATABASE_ID", "API_ENDPOINT", "MODULES_ENDPOINT", "DATABASE_NAME", "OWNER_ID"];
23
+ export type ContextEnvKey = (typeof CONTEXT_ENV_KEYS)[number];
24
+ /** Prefix for injected variables: `ACCESS_TOKEN` is far too generic to claim in
25
+ * a shared process environment, `CONSTRUCTIVE_ACCESS_TOKEN` is not. Inside a
26
+ * project `.env` the bare names stay readable (and are what the scaffolder
27
+ * writes), so both spellings resolve, prefixed winning. */
28
+ export declare const CONTEXT_ENV_PREFIX = "CONSTRUCTIVE_";
29
+ /** Values keyed by name, or a lookup function (a host with a secret store). */
30
+ export type ContextSource = Record<string, string | undefined> | ((name: string) => string | undefined);
31
+ /** Use the process environment (or any injected record) as the source. */
32
+ export declare function fromEnvironment(environment?: Record<string, string | undefined>): ContextSource;
33
+ /** Read a project `.env` as the source. The file is authoritative here — the
34
+ * key in it belongs to whatever backend wrote it, so it is not merged under
35
+ * the ambient environment. Returns null when the file is absent. */
36
+ export declare function fromEnvFile(cwd: string): Promise<ContextSource | null>;
22
37
  export type ResolveOptions = {
23
38
  /** Which plane the context is for. Ownership gates the control plane only:
24
39
  * schema editing and provisioning act under the signed-in account, so a
@@ -28,7 +43,15 @@ export type ResolveOptions = {
28
43
  * the app itself does in the Preview. */
29
44
  plane?: 'control' | 'data';
30
45
  };
31
- export declare function resolveProjectContext(cwd: string, options?: ResolveOptions): Promise<ResolveResult>;
46
+ /**
47
+ * Resolve the project context from injected values, or from a project folder.
48
+ *
49
+ * Passing a `cwd` string keeps the original behavior (read `<cwd>/.env`) so
50
+ * existing hosts — Constructive Desktop, the confirm gate — are unchanged.
51
+ * Headless hosts pass values instead: `fromEnvironment()` for a container or
52
+ * Job, an explicit record for anything else.
53
+ */
54
+ export declare function resolveProjectContext(input: string | ContextSource, options?: ResolveOptions): Promise<ResolveResult>;
32
55
  export type DataTokenResult = {
33
56
  token?: string;
34
57
  userId?: string;
package/context.js CHANGED
@@ -3,6 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CONTEXT_ENV_PREFIX = exports.CONTEXT_ENV_KEYS = void 0;
7
+ exports.fromEnvironment = fromEnvironment;
8
+ exports.fromEnvFile = fromEnvFile;
6
9
  exports.resolveProjectContext = resolveProjectContext;
7
10
  exports.resolveDataToken = resolveDataToken;
8
11
  exports.deriveSubdomainEndpoint = deriveSubdomainEndpoint;
@@ -10,52 +13,78 @@ exports.resolveOrgName = resolveOrgName;
10
13
  const promises_1 = require("node:fs/promises");
11
14
  const node_path_1 = __importDefault(require("node:path"));
12
15
  const sdk_1 = require("@constructive-io/sdk");
16
+ const dotenv_1 = require("12factor-env/dotenv");
13
17
  const db_probe_1 = require("./db-probe");
14
18
  const host_1 = require("./host");
15
19
  const DEFAULT_API_ENDPOINT = 'http://api.localhost:3000/graphql';
16
20
  const DEFAULT_MODULES_ENDPOINT = 'http://modules.localhost:3000/graphql';
17
- function parseEnv(source) {
18
- const env = {};
19
- for (const rawLine of source.split('\n')) {
20
- const line = rawLine.trim();
21
- if (!line || line.startsWith('#'))
22
- continue;
23
- const eq = line.indexOf('=');
24
- if (eq === -1)
25
- continue;
26
- const key = line.slice(0, eq).trim();
27
- let value = line.slice(eq + 1).trim();
28
- if ((value.startsWith('"') && value.endsWith('"')) ||
29
- (value.startsWith("'") && value.endsWith("'"))) {
30
- value = value.slice(1, -1);
31
- }
32
- env[key] = value;
33
- }
34
- return env;
21
+ // The project values pi needs, and where they come from. A scaffolded project
22
+ // folder carries them in `.env` (desktop); a headless host — a container, a
23
+ // Job, CI — injects them as real environment variables, so no credential is
24
+ // ever written into a git clone the agent could commit. Both lanes produce the
25
+ // same record, which is why the resolver takes VALUES, not a directory.
26
+ exports.CONTEXT_ENV_KEYS = [
27
+ 'ACCESS_TOKEN',
28
+ 'DATABASE_ID',
29
+ 'API_ENDPOINT',
30
+ 'MODULES_ENDPOINT',
31
+ 'DATABASE_NAME',
32
+ 'OWNER_ID',
33
+ ];
34
+ /** Prefix for injected variables: `ACCESS_TOKEN` is far too generic to claim in
35
+ * a shared process environment, `CONSTRUCTIVE_ACCESS_TOKEN` is not. Inside a
36
+ * project `.env` the bare names stay readable (and are what the scaffolder
37
+ * writes), so both spellings resolve, prefixed winning. */
38
+ exports.CONTEXT_ENV_PREFIX = 'CONSTRUCTIVE_';
39
+ const lookup = (source) => typeof source === 'function' ? source : (name) => source[name];
40
+ const readContextValue = (source, key) => {
41
+ const get = lookup(source);
42
+ const value = get(`${exports.CONTEXT_ENV_PREFIX}${key}`) ?? get(key);
43
+ return value ? value : undefined;
44
+ };
45
+ /** Use the process environment (or any injected record) as the source. */
46
+ function fromEnvironment(environment = process.env) {
47
+ return environment;
35
48
  }
36
- async function resolveProjectContext(cwd, options = {}) {
37
- let source;
49
+ /** Read a project `.env` as the source. The file is authoritative here — the
50
+ * key in it belongs to whatever backend wrote it, so it is not merged under
51
+ * the ambient environment. Returns null when the file is absent. */
52
+ async function fromEnvFile(cwd) {
38
53
  try {
39
- source = await (0, promises_1.readFile)(node_path_1.default.join(cwd, '.env'), 'utf8');
54
+ return (0, dotenv_1.parseDotenv)(await (0, promises_1.readFile)(node_path_1.default.join(cwd, '.env'), 'utf8'));
40
55
  }
41
56
  catch {
57
+ return null;
58
+ }
59
+ }
60
+ /**
61
+ * Resolve the project context from injected values, or from a project folder.
62
+ *
63
+ * Passing a `cwd` string keeps the original behavior (read `<cwd>/.env`) so
64
+ * existing hosts — Constructive Desktop, the confirm gate — are unchanged.
65
+ * Headless hosts pass values instead: `fromEnvironment()` for a container or
66
+ * Job, an explicit record for anything else.
67
+ */
68
+ async function resolveProjectContext(input, options = {}) {
69
+ const source = typeof input === 'string' ? await fromEnvFile(input) : input;
70
+ if (!source) {
42
71
  return {
43
72
  context: null,
44
73
  reason: 'No .env found in the project. Provision a Constructive database first (scaffold the project), then retry.',
45
74
  code: 'no-env',
46
75
  };
47
76
  }
48
- const env = parseEnv(source);
77
+ const env = Object.fromEntries(exports.CONTEXT_ENV_KEYS.map((key) => [key, readContextValue(source, key)]));
49
78
  const accessToken = env.ACCESS_TOKEN;
50
79
  const databaseId = env.DATABASE_ID;
51
80
  if (!accessToken || !databaseId) {
52
81
  return {
53
82
  context: null,
54
- reason: 'Project is not connected to a Constructive database yet (missing ACCESS_TOKEN/DATABASE_ID in .env). Provision the database first, then retry.',
83
+ reason: `Project is not connected to a Constructive database yet (missing ${exports.CONTEXT_ENV_PREFIX}ACCESS_TOKEN/${exports.CONTEXT_ENV_PREFIX}DATABASE_ID in the environment, or ACCESS_TOKEN/DATABASE_ID in .env). Provision the database first, then retry.`,
55
84
  code: 'missing-credentials',
56
85
  };
57
86
  }
58
- // A per-project .env pin wins for the DATA plane (the .env key belongs to
87
+ // A source-supplied pin wins for the DATA plane (the access key belongs to
59
88
  // whatever backend wrote it); otherwise fall back to the app's backend-config
60
89
  // store (environment-aware) so app + harness share one endpoint source.
61
90
  const host = (0, host_1.getHost)();
@@ -65,9 +94,9 @@ async function resolveProjectContext(cwd, options = {}) {
65
94
  // The whole control plane (binding probe, schema resolution, blueprint/schema
66
95
  // tools) authenticates with the ACCOUNT bearer: the platform api rejects
67
96
  // per-database keys, so the project ACCESS_TOKEN can never act on metaschema
68
- // surfaces. The .env key stays in the context for the data plane only. The
69
- // bearer is only ever sent to the app-configured backend — never a
70
- // .env-pinned endpoint, which an untrusted cloned project controls.
97
+ // surfaces. The source's key stays in the context for the data plane only.
98
+ // The bearer is only ever sent to the app-configured backend — never a
99
+ // source-pinned endpoint, which an untrusted cloned project controls.
71
100
  const controlApiEndpoint = backend?.apiEndpoint || DEFAULT_API_ENDPOINT;
72
101
  const controlModulesEndpoint = backend?.modulesEndpoint || DEFAULT_MODULES_ENDPOINT;
73
102
  const account = host.account();
@@ -81,7 +110,7 @@ async function resolveProjectContext(cwd, options = {}) {
81
110
  }
82
111
  const controlHeaders = { Authorization: `Bearer ${accountBearer}` };
83
112
  const apiClient = sdk_1.api.createClient({ endpoint: controlApiEndpoint, headers: controlHeaders });
84
- // Always probe the bound database — the .env stamp proves the project WAS
113
+ // Always probe the bound database — the DATABASE_ID stamp proves it WAS
85
114
  // bound, never that the database still exists (backend refresh, deletion,
86
115
  // revoked key). The probe is the single source of binding health.
87
116
  const probe = await (0, db_probe_1.probeDatabase)({
@@ -108,7 +137,7 @@ async function resolveProjectContext(cwd, options = {}) {
108
137
  // never shown in the Schemas tab or written to by the agent. The project's
109
138
  // local blueprint survives account changes, so recovery is a reprovision
110
139
  // under the signed-in account. Owner truth comes from the probe (backend),
111
- // not the .env stamp. Data-plane resolution skips this gate — see
140
+ // not the source's stamp. Data-plane resolution skips this gate — see
112
141
  // ResolveOptions.
113
142
  const sessionUserId = account?.userId;
114
143
  if (options.plane !== 'data' &&
package/esm/context.d.ts CHANGED
@@ -19,6 +19,21 @@ export type ResolveResult = {
19
19
  reason: string;
20
20
  code?: ProjectContextFailureCode;
21
21
  };
22
+ export declare const CONTEXT_ENV_KEYS: readonly ["ACCESS_TOKEN", "DATABASE_ID", "API_ENDPOINT", "MODULES_ENDPOINT", "DATABASE_NAME", "OWNER_ID"];
23
+ export type ContextEnvKey = (typeof CONTEXT_ENV_KEYS)[number];
24
+ /** Prefix for injected variables: `ACCESS_TOKEN` is far too generic to claim in
25
+ * a shared process environment, `CONSTRUCTIVE_ACCESS_TOKEN` is not. Inside a
26
+ * project `.env` the bare names stay readable (and are what the scaffolder
27
+ * writes), so both spellings resolve, prefixed winning. */
28
+ export declare const CONTEXT_ENV_PREFIX = "CONSTRUCTIVE_";
29
+ /** Values keyed by name, or a lookup function (a host with a secret store). */
30
+ export type ContextSource = Record<string, string | undefined> | ((name: string) => string | undefined);
31
+ /** Use the process environment (or any injected record) as the source. */
32
+ export declare function fromEnvironment(environment?: Record<string, string | undefined>): ContextSource;
33
+ /** Read a project `.env` as the source. The file is authoritative here — the
34
+ * key in it belongs to whatever backend wrote it, so it is not merged under
35
+ * the ambient environment. Returns null when the file is absent. */
36
+ export declare function fromEnvFile(cwd: string): Promise<ContextSource | null>;
22
37
  export type ResolveOptions = {
23
38
  /** Which plane the context is for. Ownership gates the control plane only:
24
39
  * schema editing and provisioning act under the signed-in account, so a
@@ -28,7 +43,15 @@ export type ResolveOptions = {
28
43
  * the app itself does in the Preview. */
29
44
  plane?: 'control' | 'data';
30
45
  };
31
- export declare function resolveProjectContext(cwd: string, options?: ResolveOptions): Promise<ResolveResult>;
46
+ /**
47
+ * Resolve the project context from injected values, or from a project folder.
48
+ *
49
+ * Passing a `cwd` string keeps the original behavior (read `<cwd>/.env`) so
50
+ * existing hosts — Constructive Desktop, the confirm gate — are unchanged.
51
+ * Headless hosts pass values instead: `fromEnvironment()` for a container or
52
+ * Job, an explicit record for anything else.
53
+ */
54
+ export declare function resolveProjectContext(input: string | ContextSource, options?: ResolveOptions): Promise<ResolveResult>;
32
55
  export type DataTokenResult = {
33
56
  token?: string;
34
57
  userId?: string;
package/esm/context.js CHANGED
@@ -1,52 +1,78 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { api, auth, modules } from '@constructive-io/sdk';
4
+ import { parseDotenv } from '12factor-env/dotenv';
4
5
  import { probeDatabase } from './db-probe';
5
6
  import { DEFAULT_DATA_TOKEN_SKEW_MS, getHost } from './host';
6
7
  const DEFAULT_API_ENDPOINT = 'http://api.localhost:3000/graphql';
7
8
  const DEFAULT_MODULES_ENDPOINT = 'http://modules.localhost:3000/graphql';
8
- function parseEnv(source) {
9
- const env = {};
10
- for (const rawLine of source.split('\n')) {
11
- const line = rawLine.trim();
12
- if (!line || line.startsWith('#'))
13
- continue;
14
- const eq = line.indexOf('=');
15
- if (eq === -1)
16
- continue;
17
- const key = line.slice(0, eq).trim();
18
- let value = line.slice(eq + 1).trim();
19
- if ((value.startsWith('"') && value.endsWith('"')) ||
20
- (value.startsWith("'") && value.endsWith("'"))) {
21
- value = value.slice(1, -1);
22
- }
23
- env[key] = value;
24
- }
25
- return env;
9
+ // The project values pi needs, and where they come from. A scaffolded project
10
+ // folder carries them in `.env` (desktop); a headless host — a container, a
11
+ // Job, CI — injects them as real environment variables, so no credential is
12
+ // ever written into a git clone the agent could commit. Both lanes produce the
13
+ // same record, which is why the resolver takes VALUES, not a directory.
14
+ export const CONTEXT_ENV_KEYS = [
15
+ 'ACCESS_TOKEN',
16
+ 'DATABASE_ID',
17
+ 'API_ENDPOINT',
18
+ 'MODULES_ENDPOINT',
19
+ 'DATABASE_NAME',
20
+ 'OWNER_ID',
21
+ ];
22
+ /** Prefix for injected variables: `ACCESS_TOKEN` is far too generic to claim in
23
+ * a shared process environment, `CONSTRUCTIVE_ACCESS_TOKEN` is not. Inside a
24
+ * project `.env` the bare names stay readable (and are what the scaffolder
25
+ * writes), so both spellings resolve, prefixed winning. */
26
+ export const CONTEXT_ENV_PREFIX = 'CONSTRUCTIVE_';
27
+ const lookup = (source) => typeof source === 'function' ? source : (name) => source[name];
28
+ const readContextValue = (source, key) => {
29
+ const get = lookup(source);
30
+ const value = get(`${CONTEXT_ENV_PREFIX}${key}`) ?? get(key);
31
+ return value ? value : undefined;
32
+ };
33
+ /** Use the process environment (or any injected record) as the source. */
34
+ export function fromEnvironment(environment = process.env) {
35
+ return environment;
26
36
  }
27
- export async function resolveProjectContext(cwd, options = {}) {
28
- let source;
37
+ /** Read a project `.env` as the source. The file is authoritative here — the
38
+ * key in it belongs to whatever backend wrote it, so it is not merged under
39
+ * the ambient environment. Returns null when the file is absent. */
40
+ export async function fromEnvFile(cwd) {
29
41
  try {
30
- source = await readFile(path.join(cwd, '.env'), 'utf8');
42
+ return parseDotenv(await readFile(path.join(cwd, '.env'), 'utf8'));
31
43
  }
32
44
  catch {
45
+ return null;
46
+ }
47
+ }
48
+ /**
49
+ * Resolve the project context from injected values, or from a project folder.
50
+ *
51
+ * Passing a `cwd` string keeps the original behavior (read `<cwd>/.env`) so
52
+ * existing hosts — Constructive Desktop, the confirm gate — are unchanged.
53
+ * Headless hosts pass values instead: `fromEnvironment()` for a container or
54
+ * Job, an explicit record for anything else.
55
+ */
56
+ export async function resolveProjectContext(input, options = {}) {
57
+ const source = typeof input === 'string' ? await fromEnvFile(input) : input;
58
+ if (!source) {
33
59
  return {
34
60
  context: null,
35
61
  reason: 'No .env found in the project. Provision a Constructive database first (scaffold the project), then retry.',
36
62
  code: 'no-env',
37
63
  };
38
64
  }
39
- const env = parseEnv(source);
65
+ const env = Object.fromEntries(CONTEXT_ENV_KEYS.map((key) => [key, readContextValue(source, key)]));
40
66
  const accessToken = env.ACCESS_TOKEN;
41
67
  const databaseId = env.DATABASE_ID;
42
68
  if (!accessToken || !databaseId) {
43
69
  return {
44
70
  context: null,
45
- reason: 'Project is not connected to a Constructive database yet (missing ACCESS_TOKEN/DATABASE_ID in .env). Provision the database first, then retry.',
71
+ reason: `Project is not connected to a Constructive database yet (missing ${CONTEXT_ENV_PREFIX}ACCESS_TOKEN/${CONTEXT_ENV_PREFIX}DATABASE_ID in the environment, or ACCESS_TOKEN/DATABASE_ID in .env). Provision the database first, then retry.`,
46
72
  code: 'missing-credentials',
47
73
  };
48
74
  }
49
- // A per-project .env pin wins for the DATA plane (the .env key belongs to
75
+ // A source-supplied pin wins for the DATA plane (the access key belongs to
50
76
  // whatever backend wrote it); otherwise fall back to the app's backend-config
51
77
  // store (environment-aware) so app + harness share one endpoint source.
52
78
  const host = getHost();
@@ -56,9 +82,9 @@ export async function resolveProjectContext(cwd, options = {}) {
56
82
  // The whole control plane (binding probe, schema resolution, blueprint/schema
57
83
  // tools) authenticates with the ACCOUNT bearer: the platform api rejects
58
84
  // per-database keys, so the project ACCESS_TOKEN can never act on metaschema
59
- // surfaces. The .env key stays in the context for the data plane only. The
60
- // bearer is only ever sent to the app-configured backend — never a
61
- // .env-pinned endpoint, which an untrusted cloned project controls.
85
+ // surfaces. The source's key stays in the context for the data plane only.
86
+ // The bearer is only ever sent to the app-configured backend — never a
87
+ // source-pinned endpoint, which an untrusted cloned project controls.
62
88
  const controlApiEndpoint = backend?.apiEndpoint || DEFAULT_API_ENDPOINT;
63
89
  const controlModulesEndpoint = backend?.modulesEndpoint || DEFAULT_MODULES_ENDPOINT;
64
90
  const account = host.account();
@@ -72,7 +98,7 @@ export async function resolveProjectContext(cwd, options = {}) {
72
98
  }
73
99
  const controlHeaders = { Authorization: `Bearer ${accountBearer}` };
74
100
  const apiClient = api.createClient({ endpoint: controlApiEndpoint, headers: controlHeaders });
75
- // Always probe the bound database — the .env stamp proves the project WAS
101
+ // Always probe the bound database — the DATABASE_ID stamp proves it WAS
76
102
  // bound, never that the database still exists (backend refresh, deletion,
77
103
  // revoked key). The probe is the single source of binding health.
78
104
  const probe = await probeDatabase({
@@ -99,7 +125,7 @@ export async function resolveProjectContext(cwd, options = {}) {
99
125
  // never shown in the Schemas tab or written to by the agent. The project's
100
126
  // local blueprint survives account changes, so recovery is a reprovision
101
127
  // under the signed-in account. Owner truth comes from the probe (backend),
102
- // not the .env stamp. Data-plane resolution skips this gate — see
128
+ // not the source's stamp. Data-plane resolution skips this gate — see
103
129
  // ResolveOptions.
104
130
  const sessionUserId = account?.userId;
105
131
  if (options.plane !== 'data' &&
package/esm/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export declare const dbTools: ExtensionFactory;
4
4
  /** Configure the host and get the extension in one call. */
5
5
  export declare function createDbTools(host: PiToolsHost): ExtensionFactory;
6
6
  export { type ConfirmGate, type ConfirmGateDeps, type ConfirmGateOptions, createConfirmGate, } from './confirm-gate';
7
- export { deriveSubdomainEndpoint, type ModulesClient, type ProjectContext, resolveDataToken, resolveProjectContext, } from './context';
7
+ export { CONTEXT_ENV_KEYS, CONTEXT_ENV_PREFIX, type ContextEnvKey, type ContextSource, deriveSubdomainEndpoint, fromEnvFile, fromEnvironment, type ModulesClient, type ProjectContext, type ProjectContextFailureCode, resolveDataToken, resolveProjectContext, } from './context';
8
8
  export { type ActiveDataToken, configureHost, type DataAuthBroker, getHost, type HostAccount, type HostBackendConfig, type HostProvisionOverlay, type PiToolsHost, type PreviewToken, } from './host';
9
9
  export { loadProvisionManifest, parseProvisionManifest, PROVISION_MANIFEST_FILE, type ProvisionManifest, } from './provision-database/manifest';
10
10
  export { allModulePresets, DEFAULT_PROVISION_PRESET, getModulePreset, type ModulePreset, type ProvisionModule, } from './provision-database/presets';
package/esm/index.js CHANGED
@@ -45,7 +45,7 @@ export function createDbTools(host) {
45
45
  return dbTools;
46
46
  }
47
47
  export { createConfirmGate, } from './confirm-gate';
48
- export { deriveSubdomainEndpoint, resolveDataToken, resolveProjectContext, } from './context';
48
+ export { CONTEXT_ENV_KEYS, CONTEXT_ENV_PREFIX, deriveSubdomainEndpoint, fromEnvFile, fromEnvironment, resolveDataToken, resolveProjectContext, } from './context';
49
49
  export { configureHost, getHost, } from './host';
50
50
  export { loadProvisionManifest, parseProvisionManifest, PROVISION_MANIFEST_FILE, } from './provision-database/manifest';
51
51
  export { allModulePresets, DEFAULT_PROVISION_PRESET, getModulePreset, } from './provision-database/presets';
package/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export declare const dbTools: ExtensionFactory;
4
4
  /** Configure the host and get the extension in one call. */
5
5
  export declare function createDbTools(host: PiToolsHost): ExtensionFactory;
6
6
  export { type ConfirmGate, type ConfirmGateDeps, type ConfirmGateOptions, createConfirmGate, } from './confirm-gate';
7
- export { deriveSubdomainEndpoint, type ModulesClient, type ProjectContext, resolveDataToken, resolveProjectContext, } from './context';
7
+ export { CONTEXT_ENV_KEYS, CONTEXT_ENV_PREFIX, type ContextEnvKey, type ContextSource, deriveSubdomainEndpoint, fromEnvFile, fromEnvironment, type ModulesClient, type ProjectContext, type ProjectContextFailureCode, resolveDataToken, resolveProjectContext, } from './context';
8
8
  export { type ActiveDataToken, configureHost, type DataAuthBroker, getHost, type HostAccount, type HostBackendConfig, type HostProvisionOverlay, type PiToolsHost, type PreviewToken, } from './host';
9
9
  export { loadProvisionManifest, parseProvisionManifest, PROVISION_MANIFEST_FILE, type ProvisionManifest, } from './provision-database/manifest';
10
10
  export { allModulePresets, DEFAULT_PROVISION_PRESET, getModulePreset, type ModulePreset, type ProvisionModule, } from './provision-database/presets';
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.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.deriveSubdomainEndpoint = 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.fromEnvironment = exports.fromEnvFile = exports.deriveSubdomainEndpoint = exports.CONTEXT_ENV_PREFIX = exports.CONTEXT_ENV_KEYS = 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");
@@ -52,7 +52,11 @@ function createDbTools(host) {
52
52
  var confirm_gate_2 = require("./confirm-gate");
53
53
  Object.defineProperty(exports, "createConfirmGate", { enumerable: true, get: function () { return confirm_gate_2.createConfirmGate; } });
54
54
  var context_2 = require("./context");
55
+ Object.defineProperty(exports, "CONTEXT_ENV_KEYS", { enumerable: true, get: function () { return context_2.CONTEXT_ENV_KEYS; } });
56
+ Object.defineProperty(exports, "CONTEXT_ENV_PREFIX", { enumerable: true, get: function () { return context_2.CONTEXT_ENV_PREFIX; } });
55
57
  Object.defineProperty(exports, "deriveSubdomainEndpoint", { enumerable: true, get: function () { return context_2.deriveSubdomainEndpoint; } });
58
+ Object.defineProperty(exports, "fromEnvFile", { enumerable: true, get: function () { return context_2.fromEnvFile; } });
59
+ Object.defineProperty(exports, "fromEnvironment", { enumerable: true, get: function () { return context_2.fromEnvironment; } });
56
60
  Object.defineProperty(exports, "resolveDataToken", { enumerable: true, get: function () { return context_2.resolveDataToken; } });
57
61
  Object.defineProperty(exports, "resolveProjectContext", { enumerable: true, get: function () { return context_2.resolveProjectContext; } });
58
62
  var host_2 = require("./host");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentic-kit/pi",
3
- "version": "0.13.0",
3
+ "version": "0.14.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
+ "12factor-env": "^1.28.0",
32
33
  "@agentic-kit/harness": "^0.12.0",
33
- "@constructive-io/graphql-query": "^4.12.10",
34
- "@constructive-io/sdk": "^1.11.11",
34
+ "@constructive-io/graphql-query": "^4.12.11",
35
+ "@constructive-io/sdk": "^1.11.12",
35
36
  "node-type-registry": "^1.16.0",
36
37
  "zod": "^4.4.3"
37
38
  },
@@ -48,5 +49,5 @@
48
49
  "coding-agent",
49
50
  "constructive"
50
51
  ],
51
- "gitHead": "6d4354f47b8bd4d3d1c2bd918a12dcb0315e1931"
52
+ "gitHead": "eacbe10ce544848c93a4c17fc8a8474d1a788a18"
52
53
  }