@lambdacurry/arbor 0.1.0 → 0.2.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/dist/arbor.js +152 -15
- package/package.json +2 -2
package/dist/arbor.js
CHANGED
|
@@ -26,13 +26,14 @@ var CONFIG_PATH = process.env.ARBOR_CONFIG ?? join(homedir(), ".arbor", "config.
|
|
|
26
26
|
var DEFAULT_API_URL = process.env.ARBOR_API_URL ?? "http://localhost:8799";
|
|
27
27
|
function loadConfig() {
|
|
28
28
|
const envToken = process.env.ARBOR_TOKEN || undefined;
|
|
29
|
+
const envUrl = process.env.ARBOR_API_URL || undefined;
|
|
29
30
|
if (existsSync(CONFIG_PATH)) {
|
|
30
31
|
try {
|
|
31
32
|
const cfg = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
|
|
32
|
-
return { apiUrl: cfg.apiUrl || DEFAULT_API_URL, token: envToken ?? cfg.token };
|
|
33
|
+
return { apiUrl: envUrl ?? (cfg.apiUrl || DEFAULT_API_URL), token: envToken ?? cfg.token };
|
|
33
34
|
} catch {}
|
|
34
35
|
}
|
|
35
|
-
return { apiUrl: DEFAULT_API_URL, token: envToken };
|
|
36
|
+
return { apiUrl: envUrl ?? DEFAULT_API_URL, token: envToken };
|
|
36
37
|
}
|
|
37
38
|
function saveConfig(cfg) {
|
|
38
39
|
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
@@ -164,6 +165,15 @@ var CONTRIBUTION_TYPES = [
|
|
|
164
165
|
"correction",
|
|
165
166
|
"decision"
|
|
166
167
|
];
|
|
168
|
+
var CONTRIBUTION_LINK_RELS = [
|
|
169
|
+
"fulfills",
|
|
170
|
+
"cites",
|
|
171
|
+
"contests",
|
|
172
|
+
"inReplyTo",
|
|
173
|
+
"produces",
|
|
174
|
+
"tracked-as",
|
|
175
|
+
"delivered-by"
|
|
176
|
+
];
|
|
167
177
|
var STAMP_FACETS = ["quality", "impact", "fit", "originality"];
|
|
168
178
|
// ../../node_modules/.pnpm/drizzle-orm@0.38.4_@cloudflare+workers-types@4.20260529.1_@prisma+client@5.22.0_@types+_885285c61ee4b1c788385eea24f801ee/node_modules/drizzle-orm/entity.js
|
|
169
179
|
var entityKind = Symbol.for("drizzle:entityKind");
|
|
@@ -1406,6 +1416,8 @@ var profiles = sqliteTable("profiles", {
|
|
|
1406
1416
|
status: text("status").$type().notNull().default("active"),
|
|
1407
1417
|
betterAuthUserId: text("better_auth_user_id").unique(),
|
|
1408
1418
|
managedByProfileId: text("managed_by_profile_id"),
|
|
1419
|
+
color: text("color").$type(),
|
|
1420
|
+
emoji: text("emoji"),
|
|
1409
1421
|
createdAt: ts("created_at").notNull()
|
|
1410
1422
|
}, (t) => ({ orgIdx: index("profiles_org_idx").on(t.orgId) }));
|
|
1411
1423
|
var clients = sqliteTable("clients", {
|
|
@@ -1533,6 +1545,8 @@ var contributions = sqliteTable("contributions", {
|
|
|
1533
1545
|
body: text("body").notNull(),
|
|
1534
1546
|
confidence: integer("confidence"),
|
|
1535
1547
|
links: text("links", { mode: "json" }).$type(),
|
|
1548
|
+
mentions: text("mentions", { mode: "json" }).$type(),
|
|
1549
|
+
attachmentIds: text("attachment_ids", { mode: "json" }).$type(),
|
|
1536
1550
|
createdAt: ts("created_at").notNull()
|
|
1537
1551
|
}, (t) => ({ threadIdx: index("contributions_thread_idx").on(t.threadId) }));
|
|
1538
1552
|
var reviews = sqliteTable("reviews", {
|
|
@@ -1559,6 +1573,7 @@ var comments = sqliteTable("comments", {
|
|
|
1559
1573
|
aboutId: text("about_id"),
|
|
1560
1574
|
anchor: text("anchor", { mode: "json" }).$type(),
|
|
1561
1575
|
replyToId: text("reply_to_id"),
|
|
1576
|
+
attachmentIds: text("attachment_ids", { mode: "json" }).$type(),
|
|
1562
1577
|
editedAt: ts("edited_at"),
|
|
1563
1578
|
deletedAt: ts("deleted_at"),
|
|
1564
1579
|
createdAt: ts("created_at").notNull()
|
|
@@ -1566,6 +1581,20 @@ var comments = sqliteTable("comments", {
|
|
|
1566
1581
|
threadIdx: index("comments_thread_idx").on(t.threadId),
|
|
1567
1582
|
aboutIdx: index("comments_about_idx").on(t.aboutType, t.aboutId)
|
|
1568
1583
|
}));
|
|
1584
|
+
var attachments = sqliteTable("attachments", {
|
|
1585
|
+
id: text("id").primaryKey(),
|
|
1586
|
+
filename: text("filename").notNull(),
|
|
1587
|
+
mimeType: text("mime_type").notNull(),
|
|
1588
|
+
size: integer("size").notNull(),
|
|
1589
|
+
r2Key: text("r2_key").notNull(),
|
|
1590
|
+
uploadedByProfileId: text("uploaded_by_profile_id").notNull().references(() => profiles.id),
|
|
1591
|
+
threadId: text("thread_id").notNull().references(() => threads.id),
|
|
1592
|
+
createdAt: ts("created_at").notNull()
|
|
1593
|
+
}, (t) => ({
|
|
1594
|
+
threadIdx: index("attachments_thread_idx").on(t.threadId),
|
|
1595
|
+
uploaderIdx: index("attachments_uploader_idx").on(t.uploadedByProfileId),
|
|
1596
|
+
r2KeyIdx: uniqueIndex("attachments_r2_key_uniq").on(t.r2Key)
|
|
1597
|
+
}));
|
|
1569
1598
|
var reactions = sqliteTable("reactions", {
|
|
1570
1599
|
id: text("id").primaryKey(),
|
|
1571
1600
|
actorProfileId: text("actor_profile_id").notNull().references(() => profiles.id),
|
|
@@ -1659,10 +1688,33 @@ var notifications = sqliteTable("notifications", {
|
|
|
1659
1688
|
createdAt: ts("created_at").notNull()
|
|
1660
1689
|
}, (t) => ({
|
|
1661
1690
|
recipientIdx: index("notifications_recipient_idx").on(t.recipientProfileId, t.readAt),
|
|
1662
|
-
groupIdx:
|
|
1691
|
+
groupIdx: uniqueIndex("notifications_group_key_uniq").on(t.groupKey)
|
|
1663
1692
|
}));
|
|
1693
|
+
var notificationCursors = sqliteTable("notification_cursors", {
|
|
1694
|
+
profileId: text("profile_id").primaryKey().references(() => profiles.id),
|
|
1695
|
+
lastSeenAt: ts("last_seen_at").notNull()
|
|
1696
|
+
});
|
|
1697
|
+
// ../core/src/ops/onboarding.ts
|
|
1698
|
+
var EMOJI_RE = new RegExp("^\\p{RGI_Emoji}$", "v");
|
|
1664
1699
|
// ../core/src/ops/agent-pairing.ts
|
|
1665
1700
|
var PAIRING_TTL_MS = 15 * 60 * 1000;
|
|
1701
|
+
// ../core/src/blob/in-memory.ts
|
|
1702
|
+
class InMemoryBlobStore {
|
|
1703
|
+
store = new Map;
|
|
1704
|
+
async put(key, bytes, mimeType) {
|
|
1705
|
+
this.store.set(key, { bytes: new Uint8Array(bytes), mimeType });
|
|
1706
|
+
}
|
|
1707
|
+
async get(key) {
|
|
1708
|
+
const blob2 = this.store.get(key);
|
|
1709
|
+
return blob2 ? { bytes: new Uint8Array(blob2.bytes), mimeType: blob2.mimeType } : null;
|
|
1710
|
+
}
|
|
1711
|
+
async delete(key) {
|
|
1712
|
+
this.store.delete(key);
|
|
1713
|
+
}
|
|
1714
|
+
get size() {
|
|
1715
|
+
return this.store.size;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1666
1718
|
// ../core/src/retrieval/in-memory.ts
|
|
1667
1719
|
function cosine(a, b) {
|
|
1668
1720
|
let dot = 0;
|
|
@@ -12610,10 +12662,10 @@ function _property(property, schema, params) {
|
|
|
12610
12662
|
...normalizeParams(params)
|
|
12611
12663
|
});
|
|
12612
12664
|
}
|
|
12613
|
-
function _mime(
|
|
12665
|
+
function _mime(types4, params) {
|
|
12614
12666
|
return new $ZodCheckMimeType({
|
|
12615
12667
|
check: "mime_type",
|
|
12616
|
-
mime:
|
|
12668
|
+
mime: types4,
|
|
12617
12669
|
...normalizeParams(params)
|
|
12618
12670
|
});
|
|
12619
12671
|
}
|
|
@@ -15159,7 +15211,7 @@ var ZodFile = /* @__PURE__ */ $constructor("ZodFile", (inst, def) => {
|
|
|
15159
15211
|
inst._zod.processJSONSchema = (ctx, json, params) => fileProcessor(inst, ctx, json, params);
|
|
15160
15212
|
inst.min = (size, params) => inst.check(_minSize(size, params));
|
|
15161
15213
|
inst.max = (size, params) => inst.check(_maxSize(size, params));
|
|
15162
|
-
inst.mime = (
|
|
15214
|
+
inst.mime = (types4, params) => inst.check(_mime(Array.isArray(types4) ? types4 : [types4], params));
|
|
15163
15215
|
});
|
|
15164
15216
|
function file(params) {
|
|
15165
15217
|
return _file(ZodFile, params);
|
|
@@ -16035,7 +16087,20 @@ var ACTIONS = [
|
|
|
16035
16087
|
threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
|
|
16036
16088
|
type: exports_external.enum(CONTRIBUTION_TYPES).describe("the contribution type (AD-066 taxonomy)"),
|
|
16037
16089
|
body: exports_external.string().min(1).describe("the contribution text"),
|
|
16038
|
-
confidence: exports_external.number().int().min(0).max(100).optional().describe("optional 0–100 confidence")
|
|
16090
|
+
confidence: exports_external.number().int().min(0).max(100).optional().describe("optional 0–100 confidence"),
|
|
16091
|
+
mentions: exports_external.array(exports_external.object({ kind: exports_external.enum(["person", "contribution", "artifact"]), id: exports_external.string(), label: exports_external.string() })).optional().describe("inline @-mentions in the body; person mentions notify"),
|
|
16092
|
+
links: exports_external.array(exports_external.union([
|
|
16093
|
+
exports_external.object({ rel: exports_external.enum(CONTRIBUTION_LINK_RELS), targetId: exports_external.string() }),
|
|
16094
|
+
exports_external.object({
|
|
16095
|
+
rel: exports_external.enum(CONTRIBUTION_LINK_RELS),
|
|
16096
|
+
external: exports_external.object({
|
|
16097
|
+
kind: exports_external.enum(["github-pr", "linear-issue", "url"]),
|
|
16098
|
+
ref: exports_external.string().describe("owner/repo#123 | SFR-312 | https url"),
|
|
16099
|
+
url: exports_external.string().optional().describe("explicit https click-through (Linear needs it)"),
|
|
16100
|
+
label: exports_external.string().min(1).max(140).describe("what the chip renders — capture the title here")
|
|
16101
|
+
})
|
|
16102
|
+
})
|
|
16103
|
+
])).optional().describe("typed links: internal { rel, targetId } (same-space) or external { rel, external: { kind, ref, url?, label } } — e.g. { rel: 'delivered-by', external: { kind: 'github-pr', ref: 'owner/repo#142', label: '#142 — ship chips' } }")
|
|
16039
16104
|
},
|
|
16040
16105
|
surfaces: ["mcp", "cli"],
|
|
16041
16106
|
run: forward("contribution.add")
|
|
@@ -16048,7 +16113,7 @@ var ACTIONS = [
|
|
|
16048
16113
|
contributionId: exports_external.string().describe("the contribution id, e.g. con_…"),
|
|
16049
16114
|
facetKey: exports_external.enum(STAMP_FACETS).optional().describe("quality|impact|fit|originality (omit for a neutral stamp)"),
|
|
16050
16115
|
polarity: exports_external.enum(["positive", "negative"]).optional().describe("required with a facet"),
|
|
16051
|
-
note: exports_external.string().min(1).describe("the one-line 'why'")
|
|
16116
|
+
note: exports_external.string().min(1).optional().describe("the one-line 'why' (required for a signed stamp)")
|
|
16052
16117
|
},
|
|
16053
16118
|
surfaces: ["mcp", "cli"],
|
|
16054
16119
|
run: forward("review.add")
|
|
@@ -16062,7 +16127,8 @@ var ACTIONS = [
|
|
|
16062
16127
|
body: exports_external.string().min(1),
|
|
16063
16128
|
aboutType: exports_external.enum(["contribution", "artifact", "comment"]).optional(),
|
|
16064
16129
|
aboutId: exports_external.string().optional(),
|
|
16065
|
-
replyToId: exports_external.string().optional()
|
|
16130
|
+
replyToId: exports_external.string().optional(),
|
|
16131
|
+
mentions: exports_external.array(exports_external.string()).optional().describe("profile ids to @-mention (they get a notification)")
|
|
16066
16132
|
},
|
|
16067
16133
|
surfaces: ["mcp", "cli"],
|
|
16068
16134
|
run: forward("comment.add")
|
|
@@ -16106,6 +16172,27 @@ var ACTIONS = [
|
|
|
16106
16172
|
surfaces: ["mcp", "cli"],
|
|
16107
16173
|
run: forward("artifact.transition")
|
|
16108
16174
|
},
|
|
16175
|
+
{
|
|
16176
|
+
name: "promote_attachment",
|
|
16177
|
+
title: "Promote a file attachment to an artifact",
|
|
16178
|
+
description: "Turn a file uploaded to a thread into a first-class artifact — the blob becomes the artifact's content, owned by the author of the contribution it hangs off (not necessarily you). Reach for this when an attached file is itself a deliverable worth retaining and citing. The attachment must share the contribution's thread; idempotent — re-promoting the same file returns the same artifact.",
|
|
16179
|
+
inputSchema: {
|
|
16180
|
+
attachmentId: exports_external.string().describe("the uploaded attachment, att_…"),
|
|
16181
|
+
sourceContributionId: exports_external.string().describe("the contribution it's produced by, con_… (must share the attachment's thread)")
|
|
16182
|
+
},
|
|
16183
|
+
surfaces: ["mcp", "cli"],
|
|
16184
|
+
run: forward("attachment.promote")
|
|
16185
|
+
},
|
|
16186
|
+
{
|
|
16187
|
+
name: "set_answer",
|
|
16188
|
+
title: "Set a thread's answer",
|
|
16189
|
+
description: "Promote a contribution to be the thread's ANSWER — it becomes a `promoted` artifact owned by that contribution's author, and the thread pins it as its resolved answer. Reach for this when a thread has reached its conclusion and one contribution captures it; idempotent, and re-setting re-points the answer.",
|
|
16190
|
+
inputSchema: {
|
|
16191
|
+
contributionId: exports_external.string().describe("the contribution to promote as the answer, con_… (its thread + author are derived)")
|
|
16192
|
+
},
|
|
16193
|
+
surfaces: ["mcp", "cli"],
|
|
16194
|
+
run: forward("thread.setAnswer")
|
|
16195
|
+
},
|
|
16109
16196
|
{
|
|
16110
16197
|
name: "request",
|
|
16111
16198
|
title: "Raise a request in a thread",
|
|
@@ -16146,6 +16233,32 @@ var ACTIONS = [
|
|
|
16146
16233
|
surfaces: ["mcp", "cli"],
|
|
16147
16234
|
run: forward("inbox.list")
|
|
16148
16235
|
},
|
|
16236
|
+
{
|
|
16237
|
+
name: "notifications",
|
|
16238
|
+
title: "List your notifications",
|
|
16239
|
+
description: "List the awareness feed of activity involving you — stamps on your work, replies in your threads, @-mentions, and your completed requests — newest first, with an unseen count. This is FYI (no response owed); use `inbox` for what's actually asked of you.",
|
|
16240
|
+
inputSchema: { ...PAGINATION_INPUT },
|
|
16241
|
+
surfaces: ["mcp", "cli"],
|
|
16242
|
+
run: forward("notification.list")
|
|
16243
|
+
},
|
|
16244
|
+
{
|
|
16245
|
+
name: "notification_read",
|
|
16246
|
+
title: "Mark a notification read",
|
|
16247
|
+
description: "Mark ONE of your notifications read by id (from `notifications`). Reach for this to clear a single awareness row you've acted on; it can only touch your own feed, never an inbox obligation.",
|
|
16248
|
+
inputSchema: {
|
|
16249
|
+
notificationId: exports_external.string().describe("the notification id, ntf_…")
|
|
16250
|
+
},
|
|
16251
|
+
surfaces: ["mcp", "cli"],
|
|
16252
|
+
run: forward("notification.markRead")
|
|
16253
|
+
},
|
|
16254
|
+
{
|
|
16255
|
+
name: "notification_read_all",
|
|
16256
|
+
title: "Mark all notifications read",
|
|
16257
|
+
description: "Mark every unread notification in your feed read in one call; returns how many were cleared. Reach for this to zero out your awareness feed after a catch-up.",
|
|
16258
|
+
inputSchema: {},
|
|
16259
|
+
surfaces: ["mcp", "cli"],
|
|
16260
|
+
run: forward("notification.markAllRead")
|
|
16261
|
+
},
|
|
16149
16262
|
{
|
|
16150
16263
|
name: "whoami",
|
|
16151
16264
|
title: "Who am I",
|
|
@@ -16154,6 +16267,17 @@ var ACTIONS = [
|
|
|
16154
16267
|
surfaces: ["mcp", "cli"],
|
|
16155
16268
|
run: forward("me.get")
|
|
16156
16269
|
},
|
|
16270
|
+
{
|
|
16271
|
+
name: "introduce",
|
|
16272
|
+
title: "Introduce yourself",
|
|
16273
|
+
description: "Set your own avatar identity (AD-029/114): an emoji that represents you and/or an identity color from the curated palette (indigo, teal, terracotta, plum, steel, olive, rose, cyan). Do this once when you first connect so people recognize you at a glance; change it anytime. Your display name stays managed by your human.",
|
|
16274
|
+
inputSchema: {
|
|
16275
|
+
emoji: exports_external.string().optional().describe("exactly one emoji that represents you, e.g. \uD83D\uDD2D"),
|
|
16276
|
+
color: exports_external.string().optional().describe("an identity color key from the curated palette")
|
|
16277
|
+
},
|
|
16278
|
+
surfaces: ["mcp", "cli"],
|
|
16279
|
+
run: forward("me.update")
|
|
16280
|
+
},
|
|
16157
16281
|
{
|
|
16158
16282
|
name: "thread_get",
|
|
16159
16283
|
title: "Read a thread",
|
|
@@ -16248,26 +16372,26 @@ var ACTIONS = [
|
|
|
16248
16372
|
{
|
|
16249
16373
|
name: "topic_create",
|
|
16250
16374
|
title: "Create a topic",
|
|
16251
|
-
description: "Create a topic inside a space to group related threads
|
|
16375
|
+
description: "Create a topic inside a space to group related threads — any space member may (AD-116), but prefer an EXISTING topic (check tree first); create one only for a genuinely new, durable sub-area, not per-task.",
|
|
16252
16376
|
inputSchema: {
|
|
16253
16377
|
spaceId: exports_external.string().min(1).describe("the parent space id"),
|
|
16254
16378
|
title: exports_external.string().min(1).describe("the topic title"),
|
|
16255
16379
|
purpose: exports_external.string().optional().describe("optional one-line purpose")
|
|
16256
16380
|
},
|
|
16257
|
-
surfaces: ["cli"],
|
|
16381
|
+
surfaces: ["mcp", "cli"],
|
|
16258
16382
|
run: forward("topic.create")
|
|
16259
16383
|
},
|
|
16260
16384
|
{
|
|
16261
16385
|
name: "thread_create",
|
|
16262
16386
|
title: "Create a thread",
|
|
16263
|
-
description: "Open a new thread under a topic with a stated objective —
|
|
16387
|
+
description: "Open a new thread under a topic with a stated objective — how you initiate work (AD-116). Check tree/recall first so you join existing work instead of duplicating it; the objective should say what done looks like.",
|
|
16264
16388
|
inputSchema: {
|
|
16265
16389
|
spaceId: exports_external.string().min(1).describe("the parent space id"),
|
|
16266
16390
|
topicId: exports_external.string().min(1).describe("the parent topic id"),
|
|
16267
16391
|
title: exports_external.string().min(1).describe("the thread title"),
|
|
16268
16392
|
objective: exports_external.string().min(1).describe("what this thread is trying to achieve")
|
|
16269
16393
|
},
|
|
16270
|
-
surfaces: ["cli"],
|
|
16394
|
+
surfaces: ["mcp", "cli"],
|
|
16271
16395
|
run: forward("thread.create")
|
|
16272
16396
|
},
|
|
16273
16397
|
{
|
|
@@ -16542,9 +16666,22 @@ function buildInput(inputSchema, flags) {
|
|
|
16542
16666
|
input[spec.field] = n;
|
|
16543
16667
|
break;
|
|
16544
16668
|
}
|
|
16545
|
-
case "array":
|
|
16546
|
-
|
|
16669
|
+
case "array": {
|
|
16670
|
+
const text2 = String(raw).trim();
|
|
16671
|
+
if (text2.startsWith("[")) {
|
|
16672
|
+
try {
|
|
16673
|
+
const parsed = JSON.parse(text2);
|
|
16674
|
+
if (Array.isArray(parsed)) {
|
|
16675
|
+
input[spec.field] = parsed;
|
|
16676
|
+
break;
|
|
16677
|
+
}
|
|
16678
|
+
} catch {
|
|
16679
|
+
throw new UsageError(`--${spec.flag} looks like JSON but failed to parse — check the quoting`);
|
|
16680
|
+
}
|
|
16681
|
+
}
|
|
16682
|
+
input[spec.field] = text2.split(",").map((s) => s.trim()).filter(Boolean);
|
|
16547
16683
|
break;
|
|
16684
|
+
}
|
|
16548
16685
|
case "boolean":
|
|
16549
16686
|
input[spec.field] = raw === true ? true : raw !== "false";
|
|
16550
16687
|
break;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lambdacurry/arbor",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The Arbor CLI
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "The Arbor CLI \u2014 a shared workspace for people and agents. The human + headless-agent write path over Arbor's guarded operation surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"arbor": "./dist/arbor.js"
|