@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
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createLegacyPrivateNetworkDoctorContract } from "klaw/plugin-sdk/ssrf-runtime";
|
|
2
|
+
//#region extensions/tlon/src/doctor-contract.ts
|
|
3
|
+
const contract = createLegacyPrivateNetworkDoctorContract({ channelKey: "tlon" });
|
|
4
|
+
const legacyConfigRules = contract.legacyConfigRules;
|
|
5
|
+
const normalizeCompatibilityConfig = contract.normalizeCompatibilityConfig;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { normalizeCompatibilityConfig as n, legacyConfigRules as t };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineBundledChannelEntry } from "klaw/plugin-sdk/channel-entry-contract";
|
|
2
|
+
//#region extensions/tlon/index.ts
|
|
3
|
+
var tlon_default = defineBundledChannelEntry({
|
|
4
|
+
id: "tlon",
|
|
5
|
+
name: "Tlon",
|
|
6
|
+
description: "Tlon/Urbit channel plugin",
|
|
7
|
+
importMetaUrl: import.meta.url,
|
|
8
|
+
plugin: {
|
|
9
|
+
specifier: "./channel-plugin-api.js",
|
|
10
|
+
exportName: "tlonPlugin"
|
|
11
|
+
},
|
|
12
|
+
runtime: {
|
|
13
|
+
specifier: "./api.js",
|
|
14
|
+
exportName: "setTlonRuntime"
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
export { tlon_default as default };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createPluginRuntimeStore } from "klaw/plugin-sdk/runtime-store";
|
|
2
|
+
//#region extensions/tlon/src/runtime.ts
|
|
3
|
+
const { setRuntime: setTlonRuntime, getRuntime: getTlonRuntime } = createPluginRuntimeStore({
|
|
4
|
+
pluginId: "tlon",
|
|
5
|
+
errorMessage: "Tlon runtime not initialized"
|
|
6
|
+
});
|
|
7
|
+
//#endregion
|
|
8
|
+
export { setTlonRuntime as n, getTlonRuntime as t };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { createDedupeCache } from "klaw/plugin-sdk/core";
|
|
2
|
+
import { createLoggerBackedRuntime } from "klaw/plugin-sdk/runtime";
|
|
3
|
+
import { SsrFBlockedError, fetchWithSsrFGuard as fetchWithSsrFGuard$1, isBlockedHostnameOrIp as isBlockedHostnameOrIp$1, ssrfPolicyFromAllowPrivateNetwork, ssrfPolicyFromDangerouslyAllowPrivateNetwork } from "klaw/plugin-sdk/ssrf-runtime";
|
|
4
|
+
export { ssrfPolicyFromAllowPrivateNetwork as a, isBlockedHostnameOrIp$1 as i, createDedupeCache as n, ssrfPolicyFromDangerouslyAllowPrivateNetwork as o, fetchWithSsrFGuard$1 as r, createLoggerBackedRuntime as s, SsrFBlockedError as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as ssrfPolicyFromAllowPrivateNetwork, i as isBlockedHostnameOrIp, n as createDedupeCache, o as ssrfPolicyFromDangerouslyAllowPrivateNetwork, r as fetchWithSsrFGuard, s as createLoggerBackedRuntime, t as SsrFBlockedError } from "./runtime-api-Dq8wkBC_.js";
|
|
2
|
+
export { SsrFBlockedError, createDedupeCache, createLoggerBackedRuntime, fetchWithSsrFGuard, isBlockedHostnameOrIp, ssrfPolicyFromAllowPrivateNetwork, ssrfPolicyFromDangerouslyAllowPrivateNetwork };
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { hasLegacyFlatAllowPrivateNetworkAlias, isBlockedHostnameOrIp, isPrivateNetworkOptInEnabled } from "klaw/plugin-sdk/ssrf-runtime";
|
|
2
|
+
import { DEFAULT_ACCOUNT_ID, createSetupInputPresenceValidator, createSetupTranslator, formatDocsLink, normalizeAccountId, patchScopedAccountConfig, prepareScopedSetupConfig } from "klaw/plugin-sdk/setup";
|
|
3
|
+
import { normalizeOptionalString, normalizeStringifiedOptionalString } from "klaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
+
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, listCombinedAccountIds, normalizeAccountId as normalizeAccountId$1, resolveMergedAccountConfig } from "klaw/plugin-sdk/account-resolution";
|
|
5
|
+
//#region extensions/tlon/src/targets.ts
|
|
6
|
+
const SHIP_RE = /^~?[a-z-]+$/i;
|
|
7
|
+
const NEST_RE = /^chat\/([^/]+)\/([^/]+)$/i;
|
|
8
|
+
function normalizeShip(raw) {
|
|
9
|
+
const trimmed = raw.trim();
|
|
10
|
+
if (!trimmed) return trimmed;
|
|
11
|
+
return trimmed.startsWith("~") ? trimmed : `~${trimmed}`;
|
|
12
|
+
}
|
|
13
|
+
function parseChannelNest(raw) {
|
|
14
|
+
const match = NEST_RE.exec(raw.trim());
|
|
15
|
+
if (!match) return null;
|
|
16
|
+
return {
|
|
17
|
+
hostShip: normalizeShip(match[1]),
|
|
18
|
+
channelName: match[2]
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function makeGroupTarget(parsed) {
|
|
22
|
+
return {
|
|
23
|
+
kind: "group",
|
|
24
|
+
nest: `chat/${parsed.hostShip}/${parsed.channelName}`,
|
|
25
|
+
hostShip: parsed.hostShip,
|
|
26
|
+
channelName: parsed.channelName
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function parseTlonTarget(raw) {
|
|
30
|
+
const trimmed = raw?.trim();
|
|
31
|
+
if (!trimmed) return null;
|
|
32
|
+
const withoutPrefix = trimmed.replace(/^tlon:/i, "");
|
|
33
|
+
const dmPrefix = withoutPrefix.match(/^dm[/:](.+)$/i);
|
|
34
|
+
if (dmPrefix) return {
|
|
35
|
+
kind: "dm",
|
|
36
|
+
ship: normalizeShip(dmPrefix[1])
|
|
37
|
+
};
|
|
38
|
+
const groupPrefix = withoutPrefix.match(/^(group|room)[/:](.+)$/i);
|
|
39
|
+
if (groupPrefix) {
|
|
40
|
+
const groupTarget = groupPrefix[2].trim();
|
|
41
|
+
if (groupTarget.startsWith("chat/")) {
|
|
42
|
+
const parsed = parseChannelNest(groupTarget);
|
|
43
|
+
if (!parsed) return null;
|
|
44
|
+
return makeGroupTarget(parsed);
|
|
45
|
+
}
|
|
46
|
+
const parts = groupTarget.split("/");
|
|
47
|
+
if (parts.length === 2) {
|
|
48
|
+
const hostShip = normalizeShip(parts[0]);
|
|
49
|
+
const channelName = parts[1];
|
|
50
|
+
return {
|
|
51
|
+
kind: "group",
|
|
52
|
+
nest: `chat/${hostShip}/${channelName}`,
|
|
53
|
+
hostShip,
|
|
54
|
+
channelName
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
if (withoutPrefix.startsWith("chat/")) {
|
|
60
|
+
const parsed = parseChannelNest(withoutPrefix);
|
|
61
|
+
if (!parsed) return null;
|
|
62
|
+
return makeGroupTarget(parsed);
|
|
63
|
+
}
|
|
64
|
+
if (SHIP_RE.test(withoutPrefix)) return {
|
|
65
|
+
kind: "dm",
|
|
66
|
+
ship: normalizeShip(withoutPrefix)
|
|
67
|
+
};
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function resolveTlonOutboundTarget(to) {
|
|
71
|
+
const parsed = parseTlonTarget(to ?? "");
|
|
72
|
+
if (!parsed) return {
|
|
73
|
+
ok: false,
|
|
74
|
+
error: /* @__PURE__ */ new Error(`Invalid Tlon target. Use ${formatTargetHint()}`)
|
|
75
|
+
};
|
|
76
|
+
if (parsed.kind === "dm") return {
|
|
77
|
+
ok: true,
|
|
78
|
+
to: parsed.ship
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
ok: true,
|
|
82
|
+
to: parsed.nest
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function formatTargetHint() {
|
|
86
|
+
return "dm/~sampel-palnet | ~sampel-palnet | chat/~host-ship/channel | group:~host-ship/channel";
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region extensions/tlon/src/account-fields.ts
|
|
90
|
+
function buildTlonAccountFields(input) {
|
|
91
|
+
return {
|
|
92
|
+
...input.ship ? { ship: input.ship } : {},
|
|
93
|
+
...input.url ? { url: input.url } : {},
|
|
94
|
+
...input.code ? { code: input.code } : {},
|
|
95
|
+
...typeof input.dangerouslyAllowPrivateNetwork === "boolean" ? { network: { dangerouslyAllowPrivateNetwork: input.dangerouslyAllowPrivateNetwork } } : {},
|
|
96
|
+
...input.groupChannels ? { groupChannels: input.groupChannels } : {},
|
|
97
|
+
...input.dmAllowlist ? { dmAllowlist: input.dmAllowlist } : {},
|
|
98
|
+
...typeof input.autoDiscoverChannels === "boolean" ? { autoDiscoverChannels: input.autoDiscoverChannels } : {},
|
|
99
|
+
...input.ownerShip ? { ownerShip: input.ownerShip } : {}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region extensions/tlon/src/types.ts
|
|
104
|
+
function resolveTlonChannelConfig(cfg) {
|
|
105
|
+
return cfg.channels?.tlon;
|
|
106
|
+
}
|
|
107
|
+
function resolveMergedTlonAccountConfig(cfg, accountId) {
|
|
108
|
+
const channel = resolveTlonChannelConfig(cfg);
|
|
109
|
+
if (accountId === DEFAULT_ACCOUNT_ID$1) return channel ?? {};
|
|
110
|
+
return resolveMergedAccountConfig({
|
|
111
|
+
channelConfig: channel ?? {},
|
|
112
|
+
accounts: channel?.accounts,
|
|
113
|
+
accountId,
|
|
114
|
+
normalizeAccountId: normalizeAccountId$1
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function resolveTlonAccount(cfg, accountId) {
|
|
118
|
+
const resolvedAccountId = normalizeAccountId$1(accountId);
|
|
119
|
+
if (!resolveTlonChannelConfig(cfg)) return {
|
|
120
|
+
accountId: resolvedAccountId,
|
|
121
|
+
name: null,
|
|
122
|
+
enabled: false,
|
|
123
|
+
configured: false,
|
|
124
|
+
ship: null,
|
|
125
|
+
url: null,
|
|
126
|
+
code: null,
|
|
127
|
+
dangerouslyAllowPrivateNetwork: null,
|
|
128
|
+
groupChannels: [],
|
|
129
|
+
dmAllowlist: [],
|
|
130
|
+
groupInviteAllowlist: [],
|
|
131
|
+
autoDiscoverChannels: null,
|
|
132
|
+
showModelSignature: null,
|
|
133
|
+
autoAcceptDmInvites: null,
|
|
134
|
+
autoAcceptGroupInvites: null,
|
|
135
|
+
defaultAuthorizedShips: [],
|
|
136
|
+
ownerShip: null
|
|
137
|
+
};
|
|
138
|
+
const merged = resolveMergedTlonAccountConfig(cfg, resolvedAccountId);
|
|
139
|
+
const ship = merged.ship ?? null;
|
|
140
|
+
const url = merged.url ?? null;
|
|
141
|
+
const code = merged.code ?? null;
|
|
142
|
+
const dangerouslyAllowPrivateNetwork = isPrivateNetworkOptInEnabled(merged) ? true : typeof merged.network?.dangerouslyAllowPrivateNetwork === "boolean" ? merged.network.dangerouslyAllowPrivateNetwork : hasLegacyFlatAllowPrivateNetworkAlias(merged) && typeof merged.allowPrivateNetwork === "boolean" ? merged.allowPrivateNetwork : null;
|
|
143
|
+
const groupChannels = merged.groupChannels ?? [];
|
|
144
|
+
const dmAllowlist = merged.dmAllowlist ?? [];
|
|
145
|
+
const groupInviteAllowlist = merged.groupInviteAllowlist ?? [];
|
|
146
|
+
const autoDiscoverChannels = merged.autoDiscoverChannels ?? null;
|
|
147
|
+
const showModelSignature = merged.showModelSignature ?? null;
|
|
148
|
+
const autoAcceptDmInvites = merged.autoAcceptDmInvites ?? null;
|
|
149
|
+
const autoAcceptGroupInvites = merged.autoAcceptGroupInvites ?? null;
|
|
150
|
+
const ownerShip = merged.ownerShip ?? null;
|
|
151
|
+
const defaultAuthorizedShips = merged.defaultAuthorizedShips ?? [];
|
|
152
|
+
const configured = Boolean(ship && url && code);
|
|
153
|
+
return {
|
|
154
|
+
accountId: resolvedAccountId,
|
|
155
|
+
name: merged.name ?? null,
|
|
156
|
+
enabled: merged.enabled !== false,
|
|
157
|
+
configured,
|
|
158
|
+
ship,
|
|
159
|
+
url,
|
|
160
|
+
code,
|
|
161
|
+
dangerouslyAllowPrivateNetwork,
|
|
162
|
+
groupChannels,
|
|
163
|
+
dmAllowlist,
|
|
164
|
+
groupInviteAllowlist,
|
|
165
|
+
autoDiscoverChannels,
|
|
166
|
+
showModelSignature,
|
|
167
|
+
autoAcceptDmInvites,
|
|
168
|
+
autoAcceptGroupInvites,
|
|
169
|
+
defaultAuthorizedShips,
|
|
170
|
+
ownerShip
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function listTlonAccountIds(cfg) {
|
|
174
|
+
const base = resolveTlonChannelConfig(cfg);
|
|
175
|
+
if (!base) return [];
|
|
176
|
+
return listCombinedAccountIds({
|
|
177
|
+
configuredAccountIds: Object.keys(base.accounts ?? {}).map(normalizeAccountId$1),
|
|
178
|
+
implicitAccountId: base.ship ? DEFAULT_ACCOUNT_ID$1 : void 0
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region extensions/tlon/src/urbit/base-url.ts
|
|
183
|
+
function hasScheme(value) {
|
|
184
|
+
return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value);
|
|
185
|
+
}
|
|
186
|
+
function normalizeUrbitHostname(hostname) {
|
|
187
|
+
return (hostname ?? "").trim().toLowerCase().replace(/\.$/, "");
|
|
188
|
+
}
|
|
189
|
+
function validateUrbitBaseUrl(raw) {
|
|
190
|
+
const trimmed = raw.trim();
|
|
191
|
+
if (!trimmed) return {
|
|
192
|
+
ok: false,
|
|
193
|
+
error: "Required"
|
|
194
|
+
};
|
|
195
|
+
const candidate = hasScheme(trimmed) ? trimmed : `https://${trimmed}`;
|
|
196
|
+
let parsed;
|
|
197
|
+
try {
|
|
198
|
+
parsed = new URL(candidate);
|
|
199
|
+
} catch {
|
|
200
|
+
return {
|
|
201
|
+
ok: false,
|
|
202
|
+
error: "Invalid URL"
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (!["http:", "https:"].includes(parsed.protocol)) return {
|
|
206
|
+
ok: false,
|
|
207
|
+
error: "URL must use http:// or https://"
|
|
208
|
+
};
|
|
209
|
+
if (parsed.username || parsed.password) return {
|
|
210
|
+
ok: false,
|
|
211
|
+
error: "URL must not include credentials"
|
|
212
|
+
};
|
|
213
|
+
const hostname = normalizeUrbitHostname(parsed.hostname);
|
|
214
|
+
if (!hostname) return {
|
|
215
|
+
ok: false,
|
|
216
|
+
error: "Invalid hostname"
|
|
217
|
+
};
|
|
218
|
+
const isIpv6 = hostname.includes(":");
|
|
219
|
+
const host = parsed.port ? `${isIpv6 ? `[${hostname}]` : hostname}:${parsed.port}` : isIpv6 ? `[${hostname}]` : hostname;
|
|
220
|
+
return {
|
|
221
|
+
ok: true,
|
|
222
|
+
baseUrl: `${parsed.protocol}//${host}`,
|
|
223
|
+
hostname
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function isBlockedUrbitHostname(hostname) {
|
|
227
|
+
const normalized = normalizeUrbitHostname(hostname);
|
|
228
|
+
if (!normalized) return false;
|
|
229
|
+
return isBlockedHostnameOrIp(normalized);
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region extensions/tlon/src/setup-core.ts
|
|
233
|
+
const t = createSetupTranslator();
|
|
234
|
+
function tlonChannelId() {
|
|
235
|
+
return "tlon";
|
|
236
|
+
}
|
|
237
|
+
function isConfigured(account) {
|
|
238
|
+
return Boolean(account.ship && account.url && account.code);
|
|
239
|
+
}
|
|
240
|
+
function createTlonSetupWizardBase(params) {
|
|
241
|
+
return {
|
|
242
|
+
channel: tlonChannelId(),
|
|
243
|
+
status: {
|
|
244
|
+
configuredLabel: t("wizard.channels.statusConfigured"),
|
|
245
|
+
unconfiguredLabel: t("wizard.channels.statusNeedsSetup"),
|
|
246
|
+
configuredHint: t("wizard.channels.statusConfigured"),
|
|
247
|
+
unconfiguredHint: t("wizard.channels.statusUrbitMessenger"),
|
|
248
|
+
configuredScore: 1,
|
|
249
|
+
unconfiguredScore: 4,
|
|
250
|
+
resolveConfigured: ({ cfg, accountId }) => params.resolveConfigured({
|
|
251
|
+
cfg,
|
|
252
|
+
accountId
|
|
253
|
+
}),
|
|
254
|
+
resolveStatusLines: ({ cfg, accountId, configured }) => params.resolveStatusLines?.({
|
|
255
|
+
cfg,
|
|
256
|
+
accountId,
|
|
257
|
+
configured
|
|
258
|
+
}) ?? []
|
|
259
|
+
},
|
|
260
|
+
introNote: {
|
|
261
|
+
title: t("wizard.tlon.setupTitle"),
|
|
262
|
+
lines: [
|
|
263
|
+
t("wizard.tlon.helpNeedsUrlCode"),
|
|
264
|
+
t("wizard.tlon.helpExampleUrl"),
|
|
265
|
+
t("wizard.tlon.helpExampleShip"),
|
|
266
|
+
t("wizard.tlon.helpPrivateNetwork"),
|
|
267
|
+
`Docs: ${formatDocsLink("/channels/tlon", "channels/tlon")}`
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
credentials: [],
|
|
271
|
+
textInputs: [
|
|
272
|
+
{
|
|
273
|
+
inputKey: "ship",
|
|
274
|
+
message: t("wizard.tlon.shipPrompt"),
|
|
275
|
+
placeholder: "~sampel-palnet",
|
|
276
|
+
currentValue: ({ cfg, accountId }) => resolveTlonAccount(cfg, accountId).ship ?? void 0,
|
|
277
|
+
validate: ({ value }) => normalizeStringifiedOptionalString(value) ? void 0 : "Required",
|
|
278
|
+
normalizeValue: ({ value }) => normalizeShip(normalizeStringifiedOptionalString(value) ?? ""),
|
|
279
|
+
applySet: async ({ cfg, accountId, value }) => applyTlonSetupConfig({
|
|
280
|
+
cfg,
|
|
281
|
+
accountId,
|
|
282
|
+
input: { ship: value }
|
|
283
|
+
})
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
inputKey: "url",
|
|
287
|
+
message: t("wizard.tlon.shipUrlPrompt"),
|
|
288
|
+
placeholder: "https://your-ship-host",
|
|
289
|
+
currentValue: ({ cfg, accountId }) => resolveTlonAccount(cfg, accountId).url ?? void 0,
|
|
290
|
+
validate: ({ value }) => {
|
|
291
|
+
const next = validateUrbitBaseUrl(value ?? "");
|
|
292
|
+
if (!next.ok) return next.error;
|
|
293
|
+
},
|
|
294
|
+
normalizeValue: ({ value }) => normalizeStringifiedOptionalString(value) ?? "",
|
|
295
|
+
applySet: async ({ cfg, accountId, value }) => applyTlonSetupConfig({
|
|
296
|
+
cfg,
|
|
297
|
+
accountId,
|
|
298
|
+
input: { url: value }
|
|
299
|
+
})
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
inputKey: "code",
|
|
303
|
+
message: t("wizard.tlon.loginCodePrompt"),
|
|
304
|
+
placeholder: "lidlut-tabwed-pillex-ridrup",
|
|
305
|
+
currentValue: ({ cfg, accountId }) => resolveTlonAccount(cfg, accountId).code ?? void 0,
|
|
306
|
+
validate: ({ value }) => normalizeStringifiedOptionalString(value) ? void 0 : "Required",
|
|
307
|
+
normalizeValue: ({ value }) => normalizeStringifiedOptionalString(value) ?? "",
|
|
308
|
+
applySet: async ({ cfg, accountId, value }) => applyTlonSetupConfig({
|
|
309
|
+
cfg,
|
|
310
|
+
accountId,
|
|
311
|
+
input: { code: value }
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
],
|
|
315
|
+
finalize: params.finalize
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
async function resolveTlonSetupConfigured(cfg, accountId) {
|
|
319
|
+
if (accountId) return isConfigured(resolveTlonAccount(cfg, accountId));
|
|
320
|
+
const accountIds = listTlonAccountIds(cfg);
|
|
321
|
+
return accountIds.length > 0 ? accountIds.some((resolvedAccountId) => isConfigured(resolveTlonAccount(cfg, resolvedAccountId))) : isConfigured(resolveTlonAccount(cfg, DEFAULT_ACCOUNT_ID));
|
|
322
|
+
}
|
|
323
|
+
async function resolveTlonSetupStatusLines(cfg, accountId) {
|
|
324
|
+
const configured = await resolveTlonSetupConfigured(cfg, accountId);
|
|
325
|
+
return [`${accountId && accountId !== DEFAULT_ACCOUNT_ID ? `Tlon (${accountId})` : "Tlon"}: ${configured ? "configured" : "needs setup"}`];
|
|
326
|
+
}
|
|
327
|
+
function applyTlonSetupConfig(params) {
|
|
328
|
+
const { cfg, accountId, input } = params;
|
|
329
|
+
const useDefault = accountId === DEFAULT_ACCOUNT_ID;
|
|
330
|
+
const namedConfig = prepareScopedSetupConfig({
|
|
331
|
+
cfg,
|
|
332
|
+
channelKey: tlonChannelId(),
|
|
333
|
+
accountId,
|
|
334
|
+
name: input.name
|
|
335
|
+
});
|
|
336
|
+
const base = namedConfig.channels?.tlon ?? {};
|
|
337
|
+
const payload = buildTlonAccountFields(input);
|
|
338
|
+
if (useDefault) return {
|
|
339
|
+
...namedConfig,
|
|
340
|
+
channels: {
|
|
341
|
+
...namedConfig.channels,
|
|
342
|
+
tlon: {
|
|
343
|
+
...base,
|
|
344
|
+
enabled: true,
|
|
345
|
+
...payload
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
return patchScopedAccountConfig({
|
|
350
|
+
cfg: namedConfig,
|
|
351
|
+
channelKey: tlonChannelId(),
|
|
352
|
+
accountId,
|
|
353
|
+
patch: { enabled: base.enabled ?? true },
|
|
354
|
+
accountPatch: {
|
|
355
|
+
enabled: true,
|
|
356
|
+
...payload
|
|
357
|
+
},
|
|
358
|
+
ensureChannelEnabled: false,
|
|
359
|
+
ensureAccountEnabled: false
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
const tlonSetupAdapter = {
|
|
363
|
+
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
|
|
364
|
+
applyAccountName: ({ cfg, accountId, name }) => prepareScopedSetupConfig({
|
|
365
|
+
cfg,
|
|
366
|
+
channelKey: tlonChannelId(),
|
|
367
|
+
accountId,
|
|
368
|
+
name
|
|
369
|
+
}),
|
|
370
|
+
validateInput: createSetupInputPresenceValidator({ validate: ({ cfg, accountId, input }) => {
|
|
371
|
+
const resolved = resolveTlonAccount(cfg, accountId ?? void 0);
|
|
372
|
+
const ship = normalizeOptionalString(input.ship) || resolved.ship;
|
|
373
|
+
const url = normalizeOptionalString(input.url) || resolved.url;
|
|
374
|
+
const code = normalizeOptionalString(input.code) || resolved.code;
|
|
375
|
+
if (!ship) return "Tlon requires --ship.";
|
|
376
|
+
if (!url) return "Tlon requires --url.";
|
|
377
|
+
if (!code) return "Tlon requires --code.";
|
|
378
|
+
return null;
|
|
379
|
+
} }),
|
|
380
|
+
applyAccountConfig: ({ cfg, accountId, input }) => applyTlonSetupConfig({
|
|
381
|
+
cfg,
|
|
382
|
+
accountId,
|
|
383
|
+
input
|
|
384
|
+
})
|
|
385
|
+
};
|
|
386
|
+
//#endregion
|
|
387
|
+
export { tlonSetupAdapter as a, validateUrbitBaseUrl as c, formatTargetHint as d, normalizeShip as f, resolveTlonOutboundTarget as h, resolveTlonSetupStatusLines as i, listTlonAccountIds as l, parseTlonTarget as m, createTlonSetupWizardBase as n, isBlockedUrbitHostname as o, parseChannelNest as p, resolveTlonSetupConfigured as r, normalizeUrbitHostname as s, applyTlonSetupConfig as t, resolveTlonAccount as u };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineBundledChannelSetupEntry } from "klaw/plugin-sdk/channel-entry-contract";
|
|
2
|
+
//#region extensions/tlon/setup-entry.ts
|
|
3
|
+
var setup_entry_default = defineBundledChannelSetupEntry({
|
|
4
|
+
importMetaUrl: import.meta.url,
|
|
5
|
+
plugin: {
|
|
6
|
+
specifier: "./api.js",
|
|
7
|
+
exportName: "tlonPlugin"
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { setup_entry_default as default };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { c as validateUrbitBaseUrl, f as normalizeShip, i as resolveTlonSetupStatusLines, n as createTlonSetupWizardBase, o as isBlockedUrbitHostname, r as resolveTlonSetupConfigured, t as applyTlonSetupConfig, u as resolveTlonAccount } from "./setup-core-CF3ryHqs.js";
|
|
2
|
+
import { createSetupTranslator } from "klaw/plugin-sdk/setup-runtime";
|
|
3
|
+
//#region extensions/tlon/src/setup-surface.ts
|
|
4
|
+
const t = createSetupTranslator();
|
|
5
|
+
function parseList(value) {
|
|
6
|
+
return value.split(/[\n,;]+/g).map((entry) => entry.trim()).filter(Boolean);
|
|
7
|
+
}
|
|
8
|
+
const tlonSetupWizard = createTlonSetupWizardBase({
|
|
9
|
+
resolveConfigured: async ({ cfg, accountId }) => await resolveTlonSetupConfigured(cfg, accountId),
|
|
10
|
+
resolveStatusLines: async ({ cfg, accountId }) => await resolveTlonSetupStatusLines(cfg, accountId),
|
|
11
|
+
finalize: async ({ cfg, accountId, prompter }) => {
|
|
12
|
+
let next = cfg;
|
|
13
|
+
const resolved = resolveTlonAccount(next, accountId);
|
|
14
|
+
const validatedUrl = validateUrbitBaseUrl(resolved.url ?? "");
|
|
15
|
+
if (!validatedUrl.ok) throw new Error(`Invalid URL: ${validatedUrl.error}`);
|
|
16
|
+
let dangerouslyAllowPrivateNetwork = resolved.dangerouslyAllowPrivateNetwork ?? false;
|
|
17
|
+
if (isBlockedUrbitHostname(validatedUrl.hostname)) {
|
|
18
|
+
dangerouslyAllowPrivateNetwork = await prompter.confirm({
|
|
19
|
+
message: t("wizard.tlon.privateNetworkPrompt"),
|
|
20
|
+
initialValue: dangerouslyAllowPrivateNetwork
|
|
21
|
+
});
|
|
22
|
+
if (!dangerouslyAllowPrivateNetwork) throw new Error("Refusing private/internal ship URL without explicit network opt-in");
|
|
23
|
+
}
|
|
24
|
+
next = applyTlonSetupConfig({
|
|
25
|
+
cfg: next,
|
|
26
|
+
accountId,
|
|
27
|
+
input: { dangerouslyAllowPrivateNetwork }
|
|
28
|
+
});
|
|
29
|
+
const currentGroups = resolved.groupChannels;
|
|
30
|
+
if (await prompter.confirm({
|
|
31
|
+
message: t("wizard.tlon.addGroupsPrompt"),
|
|
32
|
+
initialValue: currentGroups.length > 0
|
|
33
|
+
})) {
|
|
34
|
+
const entry = await prompter.text({
|
|
35
|
+
message: t("wizard.tlon.groupChannelsPrompt"),
|
|
36
|
+
placeholder: "chat/~host-ship/general, chat/~host-ship/support",
|
|
37
|
+
initialValue: currentGroups.join(", ") || void 0
|
|
38
|
+
});
|
|
39
|
+
next = applyTlonSetupConfig({
|
|
40
|
+
cfg: next,
|
|
41
|
+
accountId,
|
|
42
|
+
input: { groupChannels: parseList(entry ?? "") }
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const currentAllowlist = resolved.dmAllowlist;
|
|
46
|
+
if (await prompter.confirm({
|
|
47
|
+
message: t("wizard.tlon.restrictDmsPrompt"),
|
|
48
|
+
initialValue: currentAllowlist.length > 0
|
|
49
|
+
})) {
|
|
50
|
+
const entry = await prompter.text({
|
|
51
|
+
message: t("wizard.tlon.dmAllowlistPrompt"),
|
|
52
|
+
placeholder: "~zod, ~nec",
|
|
53
|
+
initialValue: currentAllowlist.join(", ") || void 0
|
|
54
|
+
});
|
|
55
|
+
next = applyTlonSetupConfig({
|
|
56
|
+
cfg: next,
|
|
57
|
+
accountId,
|
|
58
|
+
input: { dmAllowlist: parseList(entry ?? "").map((ship) => normalizeShip(ship)) }
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const autoDiscoverChannels = await prompter.confirm({
|
|
62
|
+
message: t("wizard.tlon.autoDiscoveryPrompt"),
|
|
63
|
+
initialValue: resolved.autoDiscoverChannels ?? true
|
|
64
|
+
});
|
|
65
|
+
next = applyTlonSetupConfig({
|
|
66
|
+
cfg: next,
|
|
67
|
+
accountId,
|
|
68
|
+
input: { autoDiscoverChannels }
|
|
69
|
+
});
|
|
70
|
+
return { cfg: next };
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
//#endregion
|
|
74
|
+
export { tlonSetupWizard as t };
|
package/dist/test-api.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { normalizeCompatibilityConfig, legacyConfigRules } from "./src/doctor-contract.js";
|
package/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineBundledChannelEntry } from "klaw/plugin-sdk/channel-entry-contract";
|
|
2
|
+
|
|
3
|
+
export default defineBundledChannelEntry({
|
|
4
|
+
id: "tlon",
|
|
5
|
+
name: "Tlon",
|
|
6
|
+
description: "Tlon/Urbit channel plugin",
|
|
7
|
+
importMetaUrl: import.meta.url,
|
|
8
|
+
plugin: {
|
|
9
|
+
specifier: "./channel-plugin-api.js",
|
|
10
|
+
exportName: "tlonPlugin",
|
|
11
|
+
},
|
|
12
|
+
runtime: {
|
|
13
|
+
specifier: "./api.js",
|
|
14
|
+
exportName: "setTlonRuntime",
|
|
15
|
+
},
|
|
16
|
+
});
|