@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.
Files changed (91) hide show
  1. package/README.md +5 -0
  2. package/api.ts +16 -0
  3. package/channel-plugin-api.ts +1 -0
  4. package/dist/api.js +4 -0
  5. package/dist/channel-Bvzym9ez.js +236 -0
  6. package/dist/channel-plugin-api.js +2 -0
  7. package/dist/channel.runtime-CDY2BdfM.js +3626 -0
  8. package/dist/doctor-contract-Ip6FcHDH.js +7 -0
  9. package/dist/doctor-contract-api.js +2 -0
  10. package/dist/index.js +18 -0
  11. package/dist/runtime-BmSb9A-q.js +8 -0
  12. package/dist/runtime-api-Dq8wkBC_.js +4 -0
  13. package/dist/runtime-api.js +2 -0
  14. package/dist/setup-api.js +3 -0
  15. package/dist/setup-core-CF3ryHqs.js +387 -0
  16. package/dist/setup-entry.js +11 -0
  17. package/dist/setup-surface-BM5_V_XL.js +74 -0
  18. package/dist/test-api.js +2 -0
  19. package/doctor-contract-api.ts +1 -0
  20. package/index.ts +16 -0
  21. package/klaw.plugin.json +3 -203
  22. package/package.json +4 -4
  23. package/runtime-api.ts +17 -0
  24. package/setup-api.ts +2 -0
  25. package/setup-entry.ts +9 -0
  26. package/src/account-fields.ts +31 -0
  27. package/src/channel.message-adapter.test.ts +145 -0
  28. package/src/channel.runtime.ts +259 -0
  29. package/src/channel.ts +192 -0
  30. package/src/config-schema.ts +54 -0
  31. package/src/core.test.ts +298 -0
  32. package/src/doctor-contract.ts +9 -0
  33. package/src/doctor.test.ts +46 -0
  34. package/src/doctor.ts +10 -0
  35. package/src/logger-runtime.ts +1 -0
  36. package/src/monitor/approval-runtime.ts +363 -0
  37. package/src/monitor/approval.test.ts +33 -0
  38. package/src/monitor/approval.ts +283 -0
  39. package/src/monitor/authorization.ts +30 -0
  40. package/src/monitor/cites.ts +54 -0
  41. package/src/monitor/discovery.ts +68 -0
  42. package/src/monitor/history.ts +226 -0
  43. package/src/monitor/index.ts +1523 -0
  44. package/src/monitor/media.test.ts +80 -0
  45. package/src/monitor/media.ts +156 -0
  46. package/src/monitor/processed-messages.test.ts +58 -0
  47. package/src/monitor/processed-messages.ts +89 -0
  48. package/src/monitor/settings-helpers.test.ts +113 -0
  49. package/src/monitor/settings-helpers.ts +158 -0
  50. package/src/monitor/utils.ts +402 -0
  51. package/src/runtime.ts +9 -0
  52. package/src/security.test.ts +658 -0
  53. package/src/session-route.ts +40 -0
  54. package/src/settings.ts +391 -0
  55. package/src/setup-core.ts +231 -0
  56. package/src/setup-surface.ts +99 -0
  57. package/src/targets.ts +102 -0
  58. package/src/tlon-api.test.ts +572 -0
  59. package/src/tlon-api.ts +389 -0
  60. package/src/types.ts +160 -0
  61. package/src/urbit/auth.ssrf.test.ts +45 -0
  62. package/src/urbit/auth.ts +48 -0
  63. package/src/urbit/base-url.test.ts +48 -0
  64. package/src/urbit/base-url.ts +61 -0
  65. package/src/urbit/channel-ops.test.ts +36 -0
  66. package/src/urbit/channel-ops.ts +149 -0
  67. package/src/urbit/context.ts +50 -0
  68. package/src/urbit/errors.ts +51 -0
  69. package/src/urbit/fetch.ts +38 -0
  70. package/src/urbit/foreigns.ts +49 -0
  71. package/src/urbit/send.test.ts +83 -0
  72. package/src/urbit/send.ts +228 -0
  73. package/src/urbit/sse-client.test.ts +234 -0
  74. package/src/urbit/sse-client.ts +492 -0
  75. package/src/urbit/story.ts +332 -0
  76. package/src/urbit/upload.test.ts +155 -0
  77. package/src/urbit/upload.ts +60 -0
  78. package/test-api.ts +1 -0
  79. package/tsconfig.json +16 -0
  80. package/api.js +0 -7
  81. package/bundled-skills/@tloncorp/tlon-skill/SKILL.md +0 -501
  82. package/bundled-skills/@tloncorp/tlon-skill/bin/tlon.js +0 -7
  83. package/bundled-skills/@tloncorp/tlon-skill/package.json +0 -40
  84. package/bundled-skills/@tloncorp/tlon-skill/scripts/postinstall.js +0 -7
  85. package/channel-plugin-api.js +0 -7
  86. package/doctor-contract-api.js +0 -7
  87. package/index.js +0 -7
  88. package/runtime-api.js +0 -7
  89. package/setup-api.js +0 -7
  90. package/setup-entry.js +0 -7
  91. package/test-api.js +0 -7
@@ -0,0 +1,99 @@
1
+ import { createSetupTranslator } from "klaw/plugin-sdk/setup-runtime";
2
+ import {
3
+ applyTlonSetupConfig,
4
+ createTlonSetupWizardBase,
5
+ resolveTlonSetupConfigured,
6
+ resolveTlonSetupStatusLines,
7
+ } from "./setup-core.js";
8
+ import { normalizeShip } from "./targets.js";
9
+ import { resolveTlonAccount } from "./types.js";
10
+ import { isBlockedUrbitHostname, validateUrbitBaseUrl } from "./urbit/base-url.js";
11
+
12
+ const t = createSetupTranslator();
13
+
14
+ function parseList(value: string): string[] {
15
+ return value
16
+ .split(/[\n,;]+/g)
17
+ .map((entry) => entry.trim())
18
+ .filter(Boolean);
19
+ }
20
+
21
+ export const tlonSetupWizard = createTlonSetupWizardBase({
22
+ resolveConfigured: async ({ cfg, accountId }) => await resolveTlonSetupConfigured(cfg, accountId),
23
+ resolveStatusLines: async ({ cfg, accountId }) =>
24
+ await resolveTlonSetupStatusLines(cfg, accountId),
25
+ finalize: async ({ cfg, accountId, prompter }) => {
26
+ let next = cfg;
27
+ const resolved = resolveTlonAccount(next, accountId);
28
+ const validatedUrl = validateUrbitBaseUrl(resolved.url ?? "");
29
+ if (!validatedUrl.ok) {
30
+ throw new Error(`Invalid URL: ${validatedUrl.error}`);
31
+ }
32
+
33
+ let dangerouslyAllowPrivateNetwork = resolved.dangerouslyAllowPrivateNetwork ?? false;
34
+ if (isBlockedUrbitHostname(validatedUrl.hostname)) {
35
+ dangerouslyAllowPrivateNetwork = await prompter.confirm({
36
+ message: t("wizard.tlon.privateNetworkPrompt"),
37
+ initialValue: dangerouslyAllowPrivateNetwork,
38
+ });
39
+ if (!dangerouslyAllowPrivateNetwork) {
40
+ throw new Error("Refusing private/internal ship URL without explicit network opt-in");
41
+ }
42
+ }
43
+ next = applyTlonSetupConfig({
44
+ cfg: next,
45
+ accountId,
46
+ input: { dangerouslyAllowPrivateNetwork },
47
+ });
48
+
49
+ const currentGroups = resolved.groupChannels;
50
+ const wantsGroupChannels = await prompter.confirm({
51
+ message: t("wizard.tlon.addGroupsPrompt"),
52
+ initialValue: currentGroups.length > 0,
53
+ });
54
+ if (wantsGroupChannels) {
55
+ const entry = await prompter.text({
56
+ message: t("wizard.tlon.groupChannelsPrompt"),
57
+ placeholder: "chat/~host-ship/general, chat/~host-ship/support",
58
+ initialValue: currentGroups.join(", ") || undefined,
59
+ });
60
+ next = applyTlonSetupConfig({
61
+ cfg: next,
62
+ accountId,
63
+ input: { groupChannels: parseList(entry ?? "") },
64
+ });
65
+ }
66
+
67
+ const currentAllowlist = resolved.dmAllowlist;
68
+ const wantsAllowlist = await prompter.confirm({
69
+ message: t("wizard.tlon.restrictDmsPrompt"),
70
+ initialValue: currentAllowlist.length > 0,
71
+ });
72
+ if (wantsAllowlist) {
73
+ const entry = await prompter.text({
74
+ message: t("wizard.tlon.dmAllowlistPrompt"),
75
+ placeholder: "~zod, ~nec",
76
+ initialValue: currentAllowlist.join(", ") || undefined,
77
+ });
78
+ next = applyTlonSetupConfig({
79
+ cfg: next,
80
+ accountId,
81
+ input: {
82
+ dmAllowlist: parseList(entry ?? "").map((ship) => normalizeShip(ship)),
83
+ },
84
+ });
85
+ }
86
+
87
+ const autoDiscoverChannels = await prompter.confirm({
88
+ message: t("wizard.tlon.autoDiscoveryPrompt"),
89
+ initialValue: resolved.autoDiscoverChannels ?? true,
90
+ });
91
+ next = applyTlonSetupConfig({
92
+ cfg: next,
93
+ accountId,
94
+ input: { autoDiscoverChannels },
95
+ });
96
+
97
+ return { cfg: next };
98
+ },
99
+ });
package/src/targets.ts ADDED
@@ -0,0 +1,102 @@
1
+ type TlonTarget =
2
+ | { kind: "dm"; ship: string }
3
+ | { kind: "group"; nest: string; hostShip: string; channelName: string };
4
+
5
+ const SHIP_RE = /^~?[a-z-]+$/i;
6
+ const NEST_RE = /^chat\/([^/]+)\/([^/]+)$/i;
7
+
8
+ export function normalizeShip(raw: string): string {
9
+ const trimmed = raw.trim();
10
+ if (!trimmed) {
11
+ return trimmed;
12
+ }
13
+ return trimmed.startsWith("~") ? trimmed : `~${trimmed}`;
14
+ }
15
+
16
+ export function parseChannelNest(raw: string): { hostShip: string; channelName: string } | null {
17
+ const match = NEST_RE.exec(raw.trim());
18
+ if (!match) {
19
+ return null;
20
+ }
21
+ const hostShip = normalizeShip(match[1]);
22
+ const channelName = match[2];
23
+ return { hostShip, channelName };
24
+ }
25
+
26
+ function makeGroupTarget(parsed: { hostShip: string; channelName: string }): TlonTarget {
27
+ return {
28
+ kind: "group",
29
+ nest: `chat/${parsed.hostShip}/${parsed.channelName}`,
30
+ hostShip: parsed.hostShip,
31
+ channelName: parsed.channelName,
32
+ };
33
+ }
34
+
35
+ export function parseTlonTarget(raw?: string | null): TlonTarget | null {
36
+ const trimmed = raw?.trim();
37
+ if (!trimmed) {
38
+ return null;
39
+ }
40
+ const withoutPrefix = trimmed.replace(/^tlon:/i, "");
41
+
42
+ const dmPrefix = withoutPrefix.match(/^dm[/:](.+)$/i);
43
+ if (dmPrefix) {
44
+ return { kind: "dm", ship: normalizeShip(dmPrefix[1]) };
45
+ }
46
+
47
+ const groupPrefix = withoutPrefix.match(/^(group|room)[/:](.+)$/i);
48
+ if (groupPrefix) {
49
+ const groupTarget = groupPrefix[2].trim();
50
+ if (groupTarget.startsWith("chat/")) {
51
+ const parsed = parseChannelNest(groupTarget);
52
+ if (!parsed) {
53
+ return null;
54
+ }
55
+ return makeGroupTarget(parsed);
56
+ }
57
+ const parts = groupTarget.split("/");
58
+ if (parts.length === 2) {
59
+ const hostShip = normalizeShip(parts[0]);
60
+ const channelName = parts[1];
61
+ return {
62
+ kind: "group",
63
+ nest: `chat/${hostShip}/${channelName}`,
64
+ hostShip,
65
+ channelName,
66
+ };
67
+ }
68
+ return null;
69
+ }
70
+
71
+ if (withoutPrefix.startsWith("chat/")) {
72
+ const parsed = parseChannelNest(withoutPrefix);
73
+ if (!parsed) {
74
+ return null;
75
+ }
76
+ return makeGroupTarget(parsed);
77
+ }
78
+
79
+ if (SHIP_RE.test(withoutPrefix)) {
80
+ return { kind: "dm", ship: normalizeShip(withoutPrefix) };
81
+ }
82
+
83
+ return null;
84
+ }
85
+
86
+ export function resolveTlonOutboundTarget(to?: string | null) {
87
+ const parsed = parseTlonTarget(to ?? "");
88
+ if (!parsed) {
89
+ return {
90
+ ok: false as const,
91
+ error: new Error(`Invalid Tlon target. Use ${formatTargetHint()}`),
92
+ };
93
+ }
94
+ if (parsed.kind === "dm") {
95
+ return { ok: true as const, to: parsed.ship };
96
+ }
97
+ return { ok: true as const, to: parsed.nest };
98
+ }
99
+
100
+ export function formatTargetHint(): string {
101
+ return "dm/~sampel-palnet | ~sampel-palnet | chat/~host-ship/channel | group:~host-ship/channel";
102
+ }