@abloatai/ablo 0.9.12 → 0.9.13

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.
@@ -283,6 +283,8 @@ export declare const ERROR_CODES: {
283
283
  readonly invalid_json: ErrorCodeSpec;
284
284
  readonly capability_id_required: ErrorCodeSpec;
285
285
  readonly organization_mismatch: ErrorCodeSpec;
286
+ readonly project_scope_denied: ErrorCodeSpec;
287
+ readonly project_slug_taken: ErrorCodeSpec;
286
288
  readonly forbidden: ErrorCodeSpec;
287
289
  readonly source_api_key_unresolved: ErrorCodeSpec;
288
290
  readonly capability_auth_disabled: ErrorCodeSpec;
@@ -305,6 +305,8 @@ export const ERROR_CODES = {
305
305
  invalid_json: wire('validation', 400, false, 'The request body was not valid JSON.'),
306
306
  capability_id_required: wire('validation', 400, false, 'A capability id is required for this request.'),
307
307
  organization_mismatch: wire('permission', 403, false, 'The request targeted an organization the caller is not scoped to.'),
308
+ project_scope_denied: wire('permission', 403, false, "The request targeted a project the caller's key is not scoped to."),
309
+ project_slug_taken: wire('validation', 409, false, 'A project with this slug already exists in the organization.'),
308
310
  forbidden: wire('permission', 403, false, 'The caller lacks permission for this operation.'),
309
311
  source_api_key_unresolved: wire('auth', 401, false, 'The source API key could not be resolved.'),
310
312
  capability_auth_disabled: wire('server', 503, false, 'Capability authentication is disabled on this server.'),
@@ -120,7 +120,13 @@ export declare function classifyCast(from: FieldType, to: FieldType): CastSafety
120
120
  * drop+add.
121
121
  */
122
122
  export declare function diffSchema(prev: SchemaJSON | null, next: SchemaJSON, hints?: RenameHints): MigrationStep[];
123
- export type WarningCode = 'drop_model' | 'drop_field' | 'risky_cast' | 'lossy_recreate' | 'enum_value_removed';
123
+ export type WarningCode = 'drop_model' | 'drop_field' | 'risky_cast' | 'lossy_recreate' | 'enum_value_removed'
124
+ /** A model disappears from what this plane's READERS resolve, without any
125
+ * table being dropped. Emitted by the server's push gate (not
126
+ * `classifyMigration`) when a first sandbox push shadows the production
127
+ * artifact that sandbox readers were served via the registry's test→live
128
+ * fallback. The data plane is untouched — the loss is visibility. */
129
+ | 'remove_model';
124
130
  export type BlockerCode = 'required_field_added' | 'made_required';
125
131
  export interface MigrationSignal {
126
132
  readonly code: WarningCode | BlockerCode;
@@ -53,6 +53,22 @@ export type SyncGroupInput = SyncGroup | `${string}:${string}` | 'default';
53
53
  export declare const syncGroupInputSchema: z.ZodUnion<readonly [z.ZodLiteral<"default">, z.core.$ZodBranded<z.ZodTemplateLiteral<`${string}:${string}`>, "SyncGroup", "out">]>;
54
54
  /** Runtime guard matching {@link SyncGroupInput}. */
55
55
  export declare function isSyncGroupInput(value: unknown): value is SyncGroupInput;
56
+ /**
57
+ * The sync-group kinds the AUTH PROVIDER mints directly onto identities — a
58
+ * CLOSED vocabulary: extend this list, never inline a new namespace string.
59
+ *
60
+ * - `org:<organizationId>` — every member of the organization
61
+ * - `user:<participantId>` — the participant itself
62
+ * - `project:<projectId>` — every credential scoped to the project
63
+ * (project axis, 2026-06-11; the org-default project's id IS the org id)
64
+ *
65
+ * Schema-declared roles ({@link identityRole} / {@link entityRole}) extend the
66
+ * vocabulary per app; these are the engine-reserved anchors.
67
+ */
68
+ export declare const IDENTITY_ANCHOR_KINDS: readonly ["org", "user", "project"];
69
+ export type IdentityAnchorKind = (typeof IDENTITY_ANCHOR_KINDS)[number];
70
+ /** Mint an engine-reserved identity anchor (typed wrapper over {@link syncGroup}). */
71
+ export declare function identityAnchor(kind: IdentityAnchorKind, id: string): SyncGroup;
56
72
  /** Validates how a role pulls ids out of a context (identity or record). */
57
73
  export declare const roleSourceSchema: z.ZodObject<{
58
74
  field: z.ZodString;
@@ -50,6 +50,24 @@ export const syncGroupInputSchema = z.union([z.literal('default'), syncGroupSche
50
50
  export function isSyncGroupInput(value) {
51
51
  return syncGroupInputSchema.safeParse(value).success;
52
52
  }
53
+ // ── Identity anchors (closed vocabulary) ────────────────────────────────────
54
+ /**
55
+ * The sync-group kinds the AUTH PROVIDER mints directly onto identities — a
56
+ * CLOSED vocabulary: extend this list, never inline a new namespace string.
57
+ *
58
+ * - `org:<organizationId>` — every member of the organization
59
+ * - `user:<participantId>` — the participant itself
60
+ * - `project:<projectId>` — every credential scoped to the project
61
+ * (project axis, 2026-06-11; the org-default project's id IS the org id)
62
+ *
63
+ * Schema-declared roles ({@link identityRole} / {@link entityRole}) extend the
64
+ * vocabulary per app; these are the engine-reserved anchors.
65
+ */
66
+ export const IDENTITY_ANCHOR_KINDS = ['org', 'user', 'project'];
67
+ /** Mint an engine-reserved identity anchor (typed wrapper over {@link syncGroup}). */
68
+ export function identityAnchor(kind, id) {
69
+ return syncGroup(kind, id);
70
+ }
53
71
  // ── Role source ─────────────────────────────────────────────────────────────
54
72
  /** Validates how a role pulls ids out of a context (identity or record). */
55
73
  export const roleSourceSchema = z.object({
package/llms.txt CHANGED
@@ -14,6 +14,10 @@ Second: make sure a key exists — WITHOUT printing it. The key is a secret; it
14
14
 
15
15
  Then PUSH — this is the step everything depends on. The server keeps its OWN copy of the schema. Run `npx ablo push --no-watch`: it pushes `ablo/schema.ts` (sandbox) AND writes `ABLO_API_KEY` into `.env.local` from the stored login. Until the schema is pushed, EVERY write to a new or changed model fails with `server_execute_unknown_model`. Re-run it after schema changes (`npx ablo push` also works once the key is wired; bare `npx ablo push` watches forever — don't, you have no TTY).
16
16
 
17
+ ## Projects (one org, many apps)
18
+
19
+ Each app gets its own PROJECT inside the org — its own schema, its own sandbox/production data planes, its own keys (the Neon/Supabase shape). `npx ablo init` creates one automatically (slug from package.json name; `--project <slug>` to choose, `--no-project` for the org default). A key belongs to exactly ONE project, fixed at mint; everything it mints inherits it. Touching another project's models fails typed: `project_scope_denied` (403) — the fix is a key minted for THAT project, never `ablo push`. Manage: `npx ablo projects list|create <slug>|use <slug|default>`; `npx ablo status` shows the active project. Wire: `GET/POST /api/v1/projects` (sk_ bearer; duplicate slug → `project_slug_taken` 409).
20
+
17
21
  ## Use this API
18
22
 
19
23
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/ablo",
3
- "version": "0.9.12",
3
+ "version": "0.9.13",
4
4
  "description": "State control API for AI agents and collaborative apps.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",