@kodelyth/tlon 2026.5.39 → 2026.5.42
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 +5 -0
- package/api.ts +16 -0
- package/channel-plugin-api.ts +1 -0
- package/dist/api.js +4 -0
- package/dist/channel-Bvzym9ez.js +236 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-CDY2BdfM.js +3626 -0
- package/dist/doctor-contract-Ip6FcHDH.js +7 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/index.js +18 -0
- package/dist/runtime-BmSb9A-q.js +8 -0
- package/dist/runtime-api-Dq8wkBC_.js +4 -0
- package/dist/runtime-api.js +2 -0
- package/dist/setup-api.js +3 -0
- package/dist/setup-core-CF3ryHqs.js +387 -0
- package/dist/setup-entry.js +11 -0
- package/dist/setup-surface-BM5_V_XL.js +74 -0
- package/dist/test-api.js +2 -0
- package/doctor-contract-api.ts +1 -0
- package/index.ts +16 -0
- package/klaw.plugin.json +3 -203
- package/package.json +4 -4
- package/runtime-api.ts +17 -0
- package/setup-api.ts +2 -0
- package/setup-entry.ts +9 -0
- package/src/account-fields.ts +31 -0
- package/src/channel.message-adapter.test.ts +145 -0
- package/src/channel.runtime.ts +259 -0
- package/src/channel.ts +192 -0
- package/src/config-schema.ts +54 -0
- package/src/core.test.ts +298 -0
- package/src/doctor-contract.ts +9 -0
- package/src/doctor.test.ts +46 -0
- package/src/doctor.ts +10 -0
- package/src/logger-runtime.ts +1 -0
- package/src/monitor/approval-runtime.ts +363 -0
- package/src/monitor/approval.test.ts +33 -0
- package/src/monitor/approval.ts +283 -0
- package/src/monitor/authorization.ts +30 -0
- package/src/monitor/cites.ts +54 -0
- package/src/monitor/discovery.ts +68 -0
- package/src/monitor/history.ts +226 -0
- package/src/monitor/index.ts +1523 -0
- package/src/monitor/media.test.ts +80 -0
- package/src/monitor/media.ts +156 -0
- package/src/monitor/processed-messages.test.ts +58 -0
- package/src/monitor/processed-messages.ts +89 -0
- package/src/monitor/settings-helpers.test.ts +113 -0
- package/src/monitor/settings-helpers.ts +158 -0
- package/src/monitor/utils.ts +402 -0
- package/src/runtime.ts +9 -0
- package/src/security.test.ts +658 -0
- package/src/session-route.ts +40 -0
- package/src/settings.ts +391 -0
- package/src/setup-core.ts +231 -0
- package/src/setup-surface.ts +99 -0
- package/src/targets.ts +102 -0
- package/src/tlon-api.test.ts +572 -0
- package/src/tlon-api.ts +389 -0
- package/src/types.ts +160 -0
- package/src/urbit/auth.ssrf.test.ts +45 -0
- package/src/urbit/auth.ts +48 -0
- package/src/urbit/base-url.test.ts +48 -0
- package/src/urbit/base-url.ts +61 -0
- package/src/urbit/channel-ops.test.ts +36 -0
- package/src/urbit/channel-ops.ts +149 -0
- package/src/urbit/context.ts +50 -0
- package/src/urbit/errors.ts +51 -0
- package/src/urbit/fetch.ts +38 -0
- package/src/urbit/foreigns.ts +49 -0
- package/src/urbit/send.test.ts +83 -0
- package/src/urbit/send.ts +228 -0
- package/src/urbit/sse-client.test.ts +234 -0
- package/src/urbit/sse-client.ts +492 -0
- package/src/urbit/story.ts +332 -0
- package/src/urbit/upload.test.ts +155 -0
- package/src/urbit/upload.ts +60 -0
- package/test-api.ts +1 -0
- package/tsconfig.json +16 -0
- package/api.js +0 -7
- package/bundled-skills/@tloncorp/tlon-skill/SKILL.md +0 -501
- package/bundled-skills/@tloncorp/tlon-skill/bin/tlon.js +0 -7
- package/bundled-skills/@tloncorp/tlon-skill/package.json +0 -40
- package/bundled-skills/@tloncorp/tlon-skill/scripts/postinstall.js +0 -7
- package/channel-plugin-api.js +0 -7
- package/doctor-contract-api.js +0 -7
- package/index.js +0 -7
- package/runtime-api.js +0 -7
- package/setup-api.js +0 -7
- package/setup-entry.js +0 -7
- package/test-api.js +0 -7
package/src/core.test.ts
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPluginSetupWizardConfigure,
|
|
3
|
+
createPluginSetupWizardStatus,
|
|
4
|
+
createTestWizardPrompter,
|
|
5
|
+
runSetupWizardConfigure,
|
|
6
|
+
} from "klaw/plugin-sdk/plugin-test-runtime";
|
|
7
|
+
import type { WizardPrompter } from "klaw/plugin-sdk/plugin-test-runtime";
|
|
8
|
+
import { describe, expect, it, vi } from "vitest";
|
|
9
|
+
import type { KlawConfig } from "../api.js";
|
|
10
|
+
import { TlonAuthorizationSchema, TlonConfigSchema } from "./config-schema.js";
|
|
11
|
+
import { tlonSetupWizard } from "./setup-surface.js";
|
|
12
|
+
import { normalizeShip, resolveTlonOutboundTarget } from "./targets.js";
|
|
13
|
+
import { listTlonAccountIds, resolveTlonAccount } from "./types.js";
|
|
14
|
+
|
|
15
|
+
const tlonTestPlugin = {
|
|
16
|
+
id: "tlon",
|
|
17
|
+
meta: { label: "Tlon" },
|
|
18
|
+
setupWizard: tlonSetupWizard,
|
|
19
|
+
config: {
|
|
20
|
+
listAccountIds: listTlonAccountIds,
|
|
21
|
+
defaultAccountId: () => "default",
|
|
22
|
+
resolveAllowFrom: ({ cfg, accountId }: { cfg: KlawConfig; accountId?: string | null }) =>
|
|
23
|
+
resolveTlonAccount(cfg, accountId).dmAllowlist,
|
|
24
|
+
formatAllowFrom: ({
|
|
25
|
+
allowFrom,
|
|
26
|
+
}: {
|
|
27
|
+
cfg: KlawConfig;
|
|
28
|
+
allowFrom: Array<string | number> | undefined | null;
|
|
29
|
+
}) => {
|
|
30
|
+
const entries: string[] = [];
|
|
31
|
+
for (const entry of allowFrom ?? []) {
|
|
32
|
+
const normalized = normalizeShip(String(entry));
|
|
33
|
+
if (normalized) {
|
|
34
|
+
entries.push(normalized);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return entries;
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
setup: {
|
|
41
|
+
resolveAccountId: ({ accountId }: { cfg: KlawConfig; accountId?: string | null }) =>
|
|
42
|
+
accountId ?? "default",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const tlonConfigure = createPluginSetupWizardConfigure(tlonTestPlugin);
|
|
47
|
+
const tlonStatus = createPluginSetupWizardStatus(tlonTestPlugin);
|
|
48
|
+
|
|
49
|
+
describe("tlon core", () => {
|
|
50
|
+
it("formats dm allowlist entries through the shared hybrid adapter", () => {
|
|
51
|
+
expect(
|
|
52
|
+
tlonTestPlugin.config.formatAllowFrom?.({
|
|
53
|
+
cfg: {} as KlawConfig,
|
|
54
|
+
allowFrom: ["zod", " ~nec "],
|
|
55
|
+
}),
|
|
56
|
+
).toEqual(["~zod", "~nec"]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("returns an empty dm allowlist when the default account is unconfigured", () => {
|
|
60
|
+
expect(
|
|
61
|
+
tlonTestPlugin.config.resolveAllowFrom?.({
|
|
62
|
+
cfg: {} as KlawConfig,
|
|
63
|
+
accountId: "default",
|
|
64
|
+
}),
|
|
65
|
+
).toStrictEqual([]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("resolves dm allowlist from the default account", () => {
|
|
69
|
+
expect(
|
|
70
|
+
tlonTestPlugin.config.resolveAllowFrom?.({
|
|
71
|
+
cfg: {
|
|
72
|
+
channels: {
|
|
73
|
+
tlon: {
|
|
74
|
+
ship: "~sampel-palnet",
|
|
75
|
+
url: "https://urbit.example.com",
|
|
76
|
+
code: "lidlut-tabwed-pillex-ridrup",
|
|
77
|
+
dmAllowlist: ["~zod"],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
} as KlawConfig,
|
|
81
|
+
accountId: "default",
|
|
82
|
+
}),
|
|
83
|
+
).toEqual(["~zod"]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("accepts channelRules with string keys", () => {
|
|
87
|
+
const parsed = TlonAuthorizationSchema.parse({
|
|
88
|
+
channelRules: {
|
|
89
|
+
"chat/~zod/test": {
|
|
90
|
+
mode: "open",
|
|
91
|
+
allowedShips: ["~zod"],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(parsed.channelRules?.["chat/~zod/test"]?.mode).toBe("open");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("accepts accounts with string keys", () => {
|
|
100
|
+
const parsed = TlonConfigSchema.parse({
|
|
101
|
+
accounts: {
|
|
102
|
+
primary: {
|
|
103
|
+
ship: "~zod",
|
|
104
|
+
url: "https://example.com",
|
|
105
|
+
code: "code-123",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(parsed.accounts?.primary?.ship).toBe("~zod");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("exposes group invite allowlists in channel config schema", () => {
|
|
114
|
+
expect(TlonConfigSchema.parse({ groupInviteAllowlist: ["~zod"] }).groupInviteAllowlist).toEqual(
|
|
115
|
+
["~zod"],
|
|
116
|
+
);
|
|
117
|
+
expect(
|
|
118
|
+
TlonConfigSchema.parse({
|
|
119
|
+
accounts: { primary: { groupInviteAllowlist: ["~nec"] } },
|
|
120
|
+
}).accounts?.primary?.groupInviteAllowlist,
|
|
121
|
+
).toEqual(["~nec"]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("configures ship, auth, and discovery settings", async () => {
|
|
125
|
+
const prompter = createTestWizardPrompter({
|
|
126
|
+
text: vi.fn(async ({ message }: { message: string }) => {
|
|
127
|
+
if (message === "Ship name") {
|
|
128
|
+
return "sampel-palnet";
|
|
129
|
+
}
|
|
130
|
+
if (message === "Ship URL") {
|
|
131
|
+
return "https://urbit.example.com";
|
|
132
|
+
}
|
|
133
|
+
if (message === "Login code") {
|
|
134
|
+
return "lidlut-tabwed-pillex-ridrup";
|
|
135
|
+
}
|
|
136
|
+
if (message === "Group channels (comma-separated)") {
|
|
137
|
+
return "chat/~host-ship/general, chat/~host-ship/support";
|
|
138
|
+
}
|
|
139
|
+
if (message === "DM allowlist (comma-separated ship names)") {
|
|
140
|
+
return "~zod, nec";
|
|
141
|
+
}
|
|
142
|
+
throw new Error(`Unexpected prompt: ${message}`);
|
|
143
|
+
}) as WizardPrompter["text"],
|
|
144
|
+
confirm: vi.fn(async ({ message }: { message: string }) => {
|
|
145
|
+
if (message === "Add group channels manually? (optional)") {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
if (message === "Restrict DMs with an allowlist?") {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
if (message === "Enable auto-discovery of group channels?") {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const result = await runSetupWizardConfigure({
|
|
159
|
+
configure: tlonConfigure,
|
|
160
|
+
cfg: {} as KlawConfig,
|
|
161
|
+
prompter,
|
|
162
|
+
options: {},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
expect(result.accountId).toBe("default");
|
|
166
|
+
expect(result.cfg.channels?.tlon?.enabled).toBe(true);
|
|
167
|
+
expect(result.cfg.channels?.tlon?.ship).toBe("~sampel-palnet");
|
|
168
|
+
expect(result.cfg.channels?.tlon?.url).toBe("https://urbit.example.com");
|
|
169
|
+
expect(result.cfg.channels?.tlon?.code).toBe("lidlut-tabwed-pillex-ridrup");
|
|
170
|
+
expect(result.cfg.channels?.tlon?.groupChannels).toEqual([
|
|
171
|
+
"chat/~host-ship/general",
|
|
172
|
+
"chat/~host-ship/support",
|
|
173
|
+
]);
|
|
174
|
+
expect(result.cfg.channels?.tlon?.dmAllowlist).toEqual(["~zod", "~nec"]);
|
|
175
|
+
expect(result.cfg.channels?.tlon?.autoDiscoverChannels).toBe(true);
|
|
176
|
+
expect(result.cfg.channels?.tlon?.network?.dangerouslyAllowPrivateNetwork).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("resolves dm targets to normalized ships", () => {
|
|
180
|
+
expect(resolveTlonOutboundTarget("dm/sampel-palnet")).toEqual({
|
|
181
|
+
ok: true,
|
|
182
|
+
to: "~sampel-palnet",
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("resolves group targets to canonical chat nests", () => {
|
|
187
|
+
expect(resolveTlonOutboundTarget("group:host-ship/general")).toEqual({
|
|
188
|
+
ok: true,
|
|
189
|
+
to: "chat/~host-ship/general",
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("returns a helpful error for invalid targets", () => {
|
|
194
|
+
const resolved = resolveTlonOutboundTarget("group:bad-target");
|
|
195
|
+
expect(resolved.ok).toBe(false);
|
|
196
|
+
if (resolved.ok) {
|
|
197
|
+
throw new Error("expected invalid target");
|
|
198
|
+
}
|
|
199
|
+
expect(resolved.error.message).toMatch(/invalid tlon target/i);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("lists named accounts and the implicit default account", () => {
|
|
203
|
+
const cfg = {
|
|
204
|
+
channels: {
|
|
205
|
+
tlon: {
|
|
206
|
+
ship: "~zod",
|
|
207
|
+
accounts: {
|
|
208
|
+
Work: { ship: "~bus" },
|
|
209
|
+
alerts: { ship: "~nec" },
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
} as KlawConfig;
|
|
214
|
+
|
|
215
|
+
expect(listTlonAccountIds(cfg)).toEqual(["alerts", "default", "work"]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("merges named account config over channel defaults", () => {
|
|
219
|
+
const resolved = resolveTlonAccount(
|
|
220
|
+
{
|
|
221
|
+
channels: {
|
|
222
|
+
tlon: {
|
|
223
|
+
name: "Base",
|
|
224
|
+
ship: "~zod",
|
|
225
|
+
url: "https://urbit.example.com",
|
|
226
|
+
code: "base-code",
|
|
227
|
+
dmAllowlist: ["~nec"],
|
|
228
|
+
groupInviteAllowlist: ["~bus"],
|
|
229
|
+
defaultAuthorizedShips: ["~marzod"],
|
|
230
|
+
accounts: {
|
|
231
|
+
Work: {
|
|
232
|
+
name: "Work",
|
|
233
|
+
code: "work-code",
|
|
234
|
+
dmAllowlist: ["~rovnys"],
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
} as KlawConfig,
|
|
240
|
+
"work",
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(resolved.accountId).toBe("work");
|
|
244
|
+
expect(resolved.name).toBe("Work");
|
|
245
|
+
expect(resolved.ship).toBe("~zod");
|
|
246
|
+
expect(resolved.url).toBe("https://urbit.example.com");
|
|
247
|
+
expect(resolved.code).toBe("work-code");
|
|
248
|
+
expect(resolved.dmAllowlist).toEqual(["~rovnys"]);
|
|
249
|
+
expect(resolved.groupInviteAllowlist).toEqual(["~bus"]);
|
|
250
|
+
expect(resolved.defaultAuthorizedShips).toEqual(["~marzod"]);
|
|
251
|
+
expect(resolved.configured).toBe(true);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("keeps the default account on channel-level config only", () => {
|
|
255
|
+
const resolved = resolveTlonAccount(
|
|
256
|
+
{
|
|
257
|
+
channels: {
|
|
258
|
+
tlon: {
|
|
259
|
+
ship: "~zod",
|
|
260
|
+
url: "https://urbit.example.com",
|
|
261
|
+
code: "base-code",
|
|
262
|
+
accounts: {
|
|
263
|
+
default: {
|
|
264
|
+
ship: "~ignored",
|
|
265
|
+
code: "ignored-code",
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
} as KlawConfig,
|
|
271
|
+
"default",
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
expect(resolved.ship).toBe("~zod");
|
|
275
|
+
expect(resolved.code).toBe("base-code");
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("setup status labels the selected account", async () => {
|
|
279
|
+
const status = await tlonStatus({
|
|
280
|
+
cfg: {
|
|
281
|
+
channels: {
|
|
282
|
+
tlon: {
|
|
283
|
+
ship: "~zod",
|
|
284
|
+
url: "https://urbit.example.com",
|
|
285
|
+
code: "base-code",
|
|
286
|
+
accounts: {
|
|
287
|
+
work: {},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
} as KlawConfig,
|
|
292
|
+
accountOverrides: { tlon: "work" },
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
expect(status.configured).toBe(true);
|
|
296
|
+
expect(status.statusLines).toEqual(["Tlon (work): configured"]);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createLegacyPrivateNetworkDoctorContract } from "klaw/plugin-sdk/ssrf-runtime";
|
|
2
|
+
|
|
3
|
+
const contract = createLegacyPrivateNetworkDoctorContract({
|
|
4
|
+
channelKey: "tlon",
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
export const legacyConfigRules = contract.legacyConfigRules;
|
|
8
|
+
|
|
9
|
+
export const normalizeCompatibilityConfig = contract.normalizeCompatibilityConfig;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { tlonDoctor } from "./doctor.js";
|
|
3
|
+
|
|
4
|
+
function getTlonCompatibilityNormalizer(): NonNullable<
|
|
5
|
+
typeof tlonDoctor.normalizeCompatibilityConfig
|
|
6
|
+
> {
|
|
7
|
+
const normalize = tlonDoctor.normalizeCompatibilityConfig;
|
|
8
|
+
if (!normalize) {
|
|
9
|
+
throw new Error("Expected tlon doctor to expose normalizeCompatibilityConfig");
|
|
10
|
+
}
|
|
11
|
+
return normalize;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe("tlon doctor", () => {
|
|
15
|
+
it("normalizes legacy private-network aliases", () => {
|
|
16
|
+
const normalize = getTlonCompatibilityNormalizer();
|
|
17
|
+
|
|
18
|
+
const result = normalize({
|
|
19
|
+
cfg: {
|
|
20
|
+
channels: {
|
|
21
|
+
tlon: {
|
|
22
|
+
allowPrivateNetwork: true,
|
|
23
|
+
accounts: {
|
|
24
|
+
alt: {
|
|
25
|
+
allowPrivateNetwork: false,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
} as never,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(result.config.channels?.tlon?.network).toEqual({
|
|
34
|
+
dangerouslyAllowPrivateNetwork: true,
|
|
35
|
+
});
|
|
36
|
+
expect(
|
|
37
|
+
(
|
|
38
|
+
result.config.channels?.tlon?.accounts?.alt as
|
|
39
|
+
| { network?: Record<string, unknown> }
|
|
40
|
+
| undefined
|
|
41
|
+
)?.network,
|
|
42
|
+
).toEqual({
|
|
43
|
+
dangerouslyAllowPrivateNetwork: false,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
package/src/doctor.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ChannelDoctorAdapter } from "klaw/plugin-sdk/channel-contract";
|
|
2
|
+
import {
|
|
3
|
+
legacyConfigRules as TLON_LEGACY_CONFIG_RULES,
|
|
4
|
+
normalizeCompatibilityConfig as normalizeTlonCompatibilityConfig,
|
|
5
|
+
} from "./doctor-contract.js";
|
|
6
|
+
|
|
7
|
+
export const tlonDoctor: ChannelDoctorAdapter = {
|
|
8
|
+
legacyConfigRules: TLON_LEGACY_CONFIG_RULES,
|
|
9
|
+
normalizeCompatibilityConfig: normalizeTlonCompatibilityConfig,
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createLoggerBackedRuntime } from "klaw/plugin-sdk/runtime";
|