@mortar-ai/skill 0.1.0 → 0.3.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 +28 -3
- package/{mortar-skill.md → SKILL.md} +116 -58
- package/bin/install.js +2 -2
- package/index.js +6 -1
- package/manifest.json +16 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @mortar-ai/skill
|
|
2
2
|
|
|
3
|
-
[
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[Agent Skills](https://agentskills.io/) package for building on **Mortar** (the
|
|
4
|
+
BaaS that AI bundles talk to). Install the canonical `SKILL.md` into a compatible
|
|
5
|
+
agent's skill directory to teach it the SDK
|
|
6
6
|
accessors, CLI commands, MCP tools, and — most importantly — the
|
|
7
7
|
tenant-isolation + `auth.tenant_id()` RLS rules that are the easiest
|
|
8
8
|
thing to get wrong.
|
|
@@ -17,6 +17,12 @@ mortar-skill-install
|
|
|
17
17
|
|
|
18
18
|
Then restart Claude Code (or `/skill reload`).
|
|
19
19
|
|
|
20
|
+
The same versioned `SKILL.md` is packaged into Fabric Harness. `manifest.json`
|
|
21
|
+
binds the npm version and Harness preinstall to its SHA-256 digest; the Fabric
|
|
22
|
+
Server rejects a Market install when the running Harness reports different
|
|
23
|
+
bytes. npm is an external distribution channel and is not required by Fabric at
|
|
24
|
+
runtime.
|
|
25
|
+
|
|
20
26
|
Pairs naturally with the [Mortar MCP server](../mcp) — the skill teaches Claude
|
|
21
27
|
*how* to think about Mortar; the MCP gives it live tools to *act* on a
|
|
22
28
|
project. Install both for the full Supabase-plugin-equivalent
|
|
@@ -29,6 +35,12 @@ The skill teaches Claude:
|
|
|
29
35
|
- **SDK surface** — the `mortar.from()` / `auth` / `storage` /
|
|
30
36
|
`realtime` / `compute` / `usage` / `cron` / `queue` / `comms`
|
|
31
37
|
accessors + the account-scope `AccountClient`; which one to reach for
|
|
38
|
+
- **Transaction boundaries** — atomic row batches and filtered mutations,
|
|
39
|
+
trusted cross-table row + PostgreSQL queue transactions, and why privileged
|
|
40
|
+
transaction clients must never enter browser/mobile bundles
|
|
41
|
+
- **Storage consistency** — immutable physical object versions, atomic metadata
|
|
42
|
+
pointer switches, durable cleanup, and why object storage is not part of a
|
|
43
|
+
PostgreSQL ACID transaction
|
|
32
44
|
- **CLI commands** — `mortar login / use / db / storage / compute /
|
|
33
45
|
cron / queue / domains / comms / keys / tokens / topup / usage`
|
|
34
46
|
- **MCP tools** — the 40 `mortar_*` tools and when an agent should use
|
|
@@ -56,6 +68,19 @@ Useful for team-shared skill repositories or in-repo skill packs.
|
|
|
56
68
|
Re-run `mortar-skill-install` after `npm update @mortar-ai/skill`.
|
|
57
69
|
Idempotent — overwrites in place.
|
|
58
70
|
|
|
71
|
+
## Maintainer release
|
|
72
|
+
|
|
73
|
+
`SKILL.md` is the only hand-edited instruction source. Regenerate the manifest
|
|
74
|
+
and Harness copy before testing or publishing:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm run sync
|
|
78
|
+
npm run check
|
|
79
|
+
npm pack --dry-run
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Never edit `fabric/harness/internal/skills/preinstalled/mortar/` directly.
|
|
83
|
+
|
|
59
84
|
## License
|
|
60
85
|
|
|
61
86
|
Apache-2.0 (same as the rest of Mortar).
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
name: mortar
|
|
3
|
+
description: Build or change Fabric applications that use Mortar for database, auth, storage, realtime, compute, or analytics. Use for Mortar schema and provisioning, SDK integration, RLS, transactions, or storage consistency; not for Fabric's own control plane or a different backend.
|
|
4
|
+
metadata:
|
|
5
|
+
activation: auto
|
|
6
|
+
priority: high
|
|
7
|
+
triggers:
|
|
8
|
+
- keywords: [database, 数据库, backend, 后端, auth, authentication, 登录, 认证, persistence, 持久化, transaction, 事务, ACID, storage, upload, 文件, realtime, 实时, collaboration, compute, cloud, 云端]
|
|
8
9
|
---
|
|
9
10
|
|
|
10
11
|
# Mortar for Fabric applications
|
|
@@ -59,14 +60,16 @@ below; if platform provisioning fails, report that platform failure.
|
|
|
59
60
|
tenant. Do not edit or create `mortar.config.json`. Do not edit generated
|
|
60
61
|
`integrations/mortar/types.ts`; Fabric regenerates it from the live schema.
|
|
61
62
|
4. Run `npm install @mortar-ai/client` if the dependency is absent, then write app
|
|
62
|
-
code using the injected singleton.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
code using the injected singleton. Atomic batches, server-side filtered
|
|
64
|
+
mutations, and `DatabaseTransactions` require `@mortar-ai/client` 0.8.0 or
|
|
65
|
+
newer. If an existing project needs one of those capabilities and pins an
|
|
66
|
+
older version, update that dependency with npm and let npm update its lockfile;
|
|
67
|
+
do not rewrite unrelated dependencies.
|
|
68
|
+
5. Before writing a multi-step mutation, choose the supported consistency
|
|
69
|
+
boundary: one-table batches and filtered mutations can use the bundle-safe
|
|
70
|
+
singleton; cross-table row + queue transactions require trusted server code;
|
|
71
|
+
object storage and external services never join a PostgreSQL transaction.
|
|
72
|
+
6. Type-check and build. Handle every `{ data, error }` result in UI code.
|
|
70
73
|
|
|
71
74
|
Supported migration kinds are `create_table`, `drop_table`, `set_table_access`,
|
|
72
75
|
`add_column`, `drop_column`, `create_index`, `set_bucket_access`, `deploy_function`, `delete_function`,
|
|
@@ -109,21 +112,86 @@ const { data: created, error: insertError } = await mortar
|
|
|
109
112
|
.insert({ title: 'Ship', done: false, user_id: userId })
|
|
110
113
|
.select()
|
|
111
114
|
.single();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Updates and deletes use the same filter chain. Useful operators include `eq`,
|
|
118
|
+
`neq`, ranges, `like/ilike`, `in`, `is`, `order`, `limit`, `single`, and
|
|
119
|
+
`maybeSingle`.
|
|
112
120
|
|
|
113
|
-
|
|
121
|
+
### Atomic row mutations
|
|
122
|
+
|
|
123
|
+
Pass an array when every insert or upsert must succeed or fail together. Mortar
|
|
124
|
+
sends at most 200 rows in one request and executes the batch in one
|
|
125
|
+
tenant-scoped PostgreSQL transaction. For upsert, a row with an `id` replaces
|
|
126
|
+
that row's data; a row without an `id` inserts. Do not use a loop or
|
|
127
|
+
`Promise.all` when partial completion would break an invariant.
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const { data: createdTodos, error: batchError } = await mortar
|
|
114
131
|
.from('todos')
|
|
115
|
-
.
|
|
116
|
-
|
|
132
|
+
.insert([
|
|
133
|
+
{ title: 'Prepare', done: false, user_id: userId },
|
|
134
|
+
{ title: 'Ship', done: false, user_id: userId },
|
|
135
|
+
])
|
|
117
136
|
.select();
|
|
137
|
+
if (batchError) throw new Error(batchError.message);
|
|
118
138
|
|
|
119
|
-
const { error:
|
|
139
|
+
const { data: changedTodos, error: updateError } = await mortar
|
|
120
140
|
.from('todos')
|
|
121
|
-
.
|
|
122
|
-
.eq('
|
|
141
|
+
.update({ done: true })
|
|
142
|
+
.eq('user_id', userId)
|
|
143
|
+
.eq('archived', false)
|
|
144
|
+
.select();
|
|
145
|
+
if (updateError) throw new Error(updateError.message);
|
|
123
146
|
```
|
|
124
147
|
|
|
125
|
-
|
|
126
|
-
|
|
148
|
+
Filtered `update` and `delete` select and mutate all matching rows inside one
|
|
149
|
+
server-side transaction. Do not pre-select IDs and then update/delete them one
|
|
150
|
+
at a time: that creates a race and loses the all-or-nothing guarantee. Chain
|
|
151
|
+
`.select()` only when the app needs the affected rows returned.
|
|
152
|
+
|
|
153
|
+
### Cross-table transactions are server-only
|
|
154
|
+
|
|
155
|
+
`DatabaseTransactions` can atomically combine up to 200 cross-table row
|
|
156
|
+
`insert` / by-ID `update` / by-ID `delete` operations and PostgreSQL-backed
|
|
157
|
+
queue `enqueue` operations. It is intentionally not attached to the injected
|
|
158
|
+
`mortar` singleton and requires an `app` or `admin` key. Its `execute` method
|
|
159
|
+
throws on failure, so use `try`/`catch`, not a `{ data, error }` envelope.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { DatabaseTransactions } from '@mortar-ai/client';
|
|
163
|
+
|
|
164
|
+
const transactions = new DatabaseTransactions(serverOnlyMortarOptions);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const results = await transactions.execute([
|
|
168
|
+
{ op: 'insert', table: 'orders', data: { customer_id: customerId, total } },
|
|
169
|
+
{ op: 'enqueue', type: 'send_receipt', payload: { customer_id: customerId } },
|
|
170
|
+
]);
|
|
171
|
+
console.log(results);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
// Nothing committed. Report or retry the whole idempotent operation.
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Never instantiate `DatabaseTransactions` in Web, Desktop renderer,
|
|
179
|
+
Keel/React Native, or WeChat client code. Never add an app/admin key to a
|
|
180
|
+
client-side environment variable or source file. Use this API only in an
|
|
181
|
+
already-approved trusted server environment that holds the key. If a Fabric
|
|
182
|
+
project has no such path, report cross-table ACID as unsupported instead of
|
|
183
|
+
simulating it with several public-client calls. A transactional queue enqueue
|
|
184
|
+
also requires Mortar's PostgreSQL queue backend; otherwise the whole request
|
|
185
|
+
fails without committing row changes.
|
|
186
|
+
|
|
187
|
+
### Exact ACID boundary
|
|
188
|
+
|
|
189
|
+
Mortar provides true ACID only for work executed in the same tenant-scoped
|
|
190
|
+
PostgreSQL transaction: atomic row batches, filtered row mutations, and the
|
|
191
|
+
trusted cross-table row + PostgreSQL queue API above. Object storage,
|
|
192
|
+
compute/functions, communications providers, analytics delivery, and other
|
|
193
|
+
external systems do not participate in that transaction. Never describe a
|
|
194
|
+
workflow spanning them as one ACID transaction or claim two-phase commit.
|
|
127
195
|
|
|
128
196
|
## App-user authentication
|
|
129
197
|
|
|
@@ -139,15 +207,12 @@ const verified = await mortar.auth.verifyOtp({ email, token });
|
|
|
139
207
|
|
|
140
208
|
const { data: sessionData } = await mortar.auth.getSession();
|
|
141
209
|
const { data: userData, error: userError } = await mortar.auth.getUser();
|
|
142
|
-
|
|
143
|
-
const { data: listener } = mortar.auth.onAuthStateChange((event, session) => {
|
|
144
|
-
// INITIAL_SESSION | SIGNED_IN | SIGNED_OUT | TOKEN_REFRESHED
|
|
145
|
-
});
|
|
146
|
-
listener.subscription.unsubscribe();
|
|
147
|
-
|
|
148
210
|
await mortar.auth.signOut();
|
|
149
211
|
```
|
|
150
212
|
|
|
213
|
+
Use `onAuthStateChange` when UI must react to session changes, and unsubscribe
|
|
214
|
+
the returned subscription during cleanup.
|
|
215
|
+
|
|
151
216
|
Password reset starts with `mortar.auth.resetPasswordForEmail(email)`; confirm via
|
|
152
217
|
`passwordResetConfirm(email, token, newPassword)`. Never store or pass session
|
|
153
218
|
tokens manually unless the user explicitly needs a server integration.
|
|
@@ -171,6 +236,22 @@ const { data: signed, error: signError } = await bucket.createSignedUrl(path, 36
|
|
|
171
236
|
const { error: removeError } = await bucket.remove([path]);
|
|
172
237
|
```
|
|
173
238
|
|
|
239
|
+
Applications address files by their stable logical `path`. Mortar stores new
|
|
240
|
+
uploads under immutable internal physical versions, then atomically switches
|
|
241
|
+
its PostgreSQL metadata pointer and durably schedules orphan/replaced versions
|
|
242
|
+
for retryable cleanup. Never construct, persist, or expose Mortar's internal
|
|
243
|
+
physical object key. Existing logical paths remain compatible, and application
|
|
244
|
+
projects do not run or author the platform storage migration.
|
|
245
|
+
|
|
246
|
+
This protocol keeps Mortar's own file metadata from pointing at a partial
|
|
247
|
+
upload, but it does not make a separate application row and an object upload
|
|
248
|
+
one transaction. When a business row must reference a file, generate an
|
|
249
|
+
unguessable logical key. For a simple workflow, upload first and write the
|
|
250
|
+
business row only after upload succeeds. For recoverable workflows, create a
|
|
251
|
+
row with a stable idempotency key and `pending` state, upload, then mark it
|
|
252
|
+
`ready`; reconcile stale rows to `failed` or retry them. Do not issue a database
|
|
253
|
+
write and `bucket.upload()` concurrently and call the result atomic.
|
|
254
|
+
|
|
174
255
|
Downloads mint a short-lived URL and fetch the object directly from the storage
|
|
175
256
|
backend. Reuse `signed.signedUrl` until it nears expiry when rendering media.
|
|
176
257
|
Prefer the signed URL directly in `<img src>`, `<video src>`, or `<audio src>`;
|
|
@@ -180,16 +261,9 @@ budget, but clients should still lazy-load visible media and cache/reuse each
|
|
|
180
261
|
URL instead of signing again on every render. Signing authorizes a possible
|
|
181
262
|
download; it does not prove bytes were served and must never be described or
|
|
182
263
|
modeled as an object-egress charge. Only provider-observed access-log or billing
|
|
183
|
-
reconciliation may record actual object egress.
|
|
184
|
-
`createSignedUrl` argument
|
|
185
|
-
a deadline
|
|
186
|
-
|
|
187
|
-
```ts
|
|
188
|
-
const { data: signed, error } = await bucket.createSignedUrl(path, 900, {
|
|
189
|
-
signal: controller.signal,
|
|
190
|
-
timeoutMs: 10_000,
|
|
191
|
-
});
|
|
192
|
-
```
|
|
264
|
+
reconciliation may record actual object egress. The optional third
|
|
265
|
+
`createSignedUrl` argument (`{ signal, timeoutMs }`) bounds only the signing
|
|
266
|
+
request when the UI needs a deadline.
|
|
193
267
|
|
|
194
268
|
`bucket.download(path)` uses JavaScript `fetch()` against the object store and
|
|
195
269
|
therefore needs a matching bucket CORS rule in browsers. Appunvs-managed Mortar
|
|
@@ -223,26 +297,10 @@ realtime only notifies peers.
|
|
|
223
297
|
|
|
224
298
|
## Compute and analytics
|
|
225
299
|
|
|
226
|
-
Invoke only functions already deployed through `mortar_migrate
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
name: 'summarize',
|
|
231
|
-
method: 'POST',
|
|
232
|
-
body: JSON.stringify({ text }),
|
|
233
|
-
contentType: 'application/json',
|
|
234
|
-
});
|
|
235
|
-
if (!response.ok) throw new Error(await response.text());
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
Analytics calls are fire-and-forget except `flush`:
|
|
239
|
-
|
|
240
|
-
```ts
|
|
241
|
-
mortar.analytics.track('pageview', { path: location.pathname });
|
|
242
|
-
mortar.analytics.identify(user.id, { plan: 'free' });
|
|
243
|
-
mortar.analytics.captureError(error, { screen: 'checkout' });
|
|
244
|
-
await mortar.analytics.flush();
|
|
245
|
-
```
|
|
300
|
+
Invoke only functions already deployed through `mortar_migrate`; check
|
|
301
|
+
`response.ok` and read the response body. Analytics provides `track`,
|
|
302
|
+
`identify`, `captureError`, and `flush`; events are fire-and-forget except
|
|
303
|
+
`flush`.
|
|
246
304
|
|
|
247
305
|
## Boundary for non-Fabric development
|
|
248
306
|
|
package/bin/install.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* mortar-skill-install — copies
|
|
3
|
+
* mortar-skill-install — copies the canonical SKILL.md into the user's Claude
|
|
4
4
|
* Code skills directory.
|
|
5
5
|
*
|
|
6
6
|
* Default destination: `~/.claude/skills/mortar/SKILL.md`. Override
|
|
@@ -26,7 +26,7 @@ function parseFlags(argv) {
|
|
|
26
26
|
|
|
27
27
|
function main() {
|
|
28
28
|
const flags = parseFlags(process.argv);
|
|
29
|
-
const src = resolve(__dirname, '..', '
|
|
29
|
+
const src = resolve(__dirname, '..', 'SKILL.md');
|
|
30
30
|
if (!existsSync(src)) {
|
|
31
31
|
console.error(`mortar-skill: source missing at ${src}`);
|
|
32
32
|
process.exit(1);
|
package/index.js
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
const { readFileSync } = require('node:fs');
|
|
10
10
|
const { resolve } = require('node:path');
|
|
11
11
|
|
|
12
|
-
const SKILL_PATH = resolve(__dirname, '
|
|
12
|
+
const SKILL_PATH = resolve(__dirname, 'SKILL.md');
|
|
13
|
+
const MANIFEST_PATH = resolve(__dirname, 'manifest.json');
|
|
13
14
|
|
|
14
15
|
module.exports = {
|
|
15
16
|
/**
|
|
@@ -20,5 +21,9 @@ module.exports = {
|
|
|
20
21
|
skillContents() {
|
|
21
22
|
return readFileSync(SKILL_PATH, 'utf8');
|
|
22
23
|
},
|
|
24
|
+
manifest() {
|
|
25
|
+
return JSON.parse(readFileSync(MANIFEST_PATH, 'utf8'));
|
|
26
|
+
},
|
|
23
27
|
SKILL_PATH,
|
|
28
|
+
MANIFEST_PATH,
|
|
24
29
|
};
|
package/manifest.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"id": "mortar",
|
|
4
|
+
"version": "0.3.0",
|
|
5
|
+
"content_sha256": "sha256:c69dab74ad4a377086c0c883c4d400770600738de2741778fad21ec27e925f27",
|
|
6
|
+
"source_repo": "liamxujia/appunvs",
|
|
7
|
+
"source_path": "mortar/skill/SKILL.md",
|
|
8
|
+
"distributions": [
|
|
9
|
+
"harness_preinstalled",
|
|
10
|
+
"npm"
|
|
11
|
+
],
|
|
12
|
+
"npm": {
|
|
13
|
+
"package": "@mortar-ai/skill",
|
|
14
|
+
"version": "0.3.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mortar-ai/skill",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Agent skill package for building Fabric applications on Mortar (BaaS), distributed from one versioned SKILL.md with a content-addressed manifest.",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"files": ["
|
|
6
|
+
"files": ["SKILL.md", "manifest.json", "index.js", "README.md"],
|
|
7
7
|
"bin": {
|
|
8
8
|
"mortar-skill-install": "bin/install.js"
|
|
9
9
|
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"sync": "node scripts/sync-distributions.js",
|
|
12
|
+
"check": "node scripts/sync-distributions.js --check"
|
|
13
|
+
},
|
|
10
14
|
"publishConfig": { "access": "public" },
|
|
11
15
|
"license": "Apache-2.0"
|
|
12
16
|
}
|