openclaw-telegram-manager 1.0.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/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/commands/archive.d.ts +4 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +71 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/doctor-all.d.ts +3 -0
- package/dist/commands/doctor-all.d.ts.map +1 -0
- package/dist/commands/doctor-all.js +193 -0
- package/dist/commands/doctor-all.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +74 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/help.d.ts +4 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +8 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/init.d.ts +17 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +304 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +22 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/rename.d.ts +3 -0
- package/dist/commands/rename.d.ts.map +1 -0
- package/dist/commands/rename.js +115 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/commands/snooze.d.ts +3 -0
- package/dist/commands/snooze.d.ts.map +1 -0
- package/dist/commands/snooze.js +52 -0
- package/dist/commands/snooze.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +3 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +38 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/upgrade.d.ts +3 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +52 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audit.d.ts +12 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +35 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/auth.d.ts +26 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +73 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/capsule.d.ts +27 -0
- package/dist/lib/capsule.d.ts.map +1 -0
- package/dist/lib/capsule.js +130 -0
- package/dist/lib/capsule.js.map +1 -0
- package/dist/lib/config-restart.d.ts +23 -0
- package/dist/lib/config-restart.d.ts.map +1 -0
- package/dist/lib/config-restart.js +129 -0
- package/dist/lib/config-restart.js.map +1 -0
- package/dist/lib/doctor-checks.d.ts +50 -0
- package/dist/lib/doctor-checks.d.ts.map +1 -0
- package/dist/lib/doctor-checks.js +421 -0
- package/dist/lib/doctor-checks.js.map +1 -0
- package/dist/lib/include-generator.d.ts +35 -0
- package/dist/lib/include-generator.d.ts.map +1 -0
- package/dist/lib/include-generator.js +140 -0
- package/dist/lib/include-generator.js.map +1 -0
- package/dist/lib/registry.d.ts +27 -0
- package/dist/lib/registry.d.ts.map +1 -0
- package/dist/lib/registry.js +154 -0
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/security.d.ts +57 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +133 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/telegram.d.ts +55 -0
- package/dist/lib/telegram.d.ts.map +1 -0
- package/dist/lib/telegram.js +254 -0
- package/dist/lib/telegram.js.map +1 -0
- package/dist/lib/types.d.ts +120 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +85 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/setup.d.ts +3 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +333 -0
- package/dist/setup.js.map +1 -0
- package/dist/tool.d.ts +15 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +201 -0
- package/dist/tool.js.map +1 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +48 -0
- package/skills/topic/SKILL.md +35 -0
- package/src/commands/archive.ts +89 -0
- package/src/commands/doctor-all.ts +243 -0
- package/src/commands/doctor.ts +100 -0
- package/src/commands/help.ts +11 -0
- package/src/commands/init.ts +376 -0
- package/src/commands/list.ts +28 -0
- package/src/commands/rename.ts +140 -0
- package/src/commands/snooze.ts +69 -0
- package/src/commands/status.ts +59 -0
- package/src/commands/sync.ts +46 -0
- package/src/commands/upgrade.ts +64 -0
- package/src/index.ts +54 -0
- package/src/lib/audit.ts +44 -0
- package/src/lib/auth.ts +96 -0
- package/src/lib/capsule.ts +206 -0
- package/src/lib/config-restart.ts +167 -0
- package/src/lib/doctor-checks.ts +639 -0
- package/src/lib/include-generator.ts +174 -0
- package/src/lib/registry.ts +197 -0
- package/src/lib/security.ts +174 -0
- package/src/lib/telegram.ts +311 -0
- package/src/lib/types.ts +172 -0
- package/src/setup.ts +402 -0
- package/src/templates/base/COMMANDS.md +3 -0
- package/src/templates/base/CRON.md +3 -0
- package/src/templates/base/LINKS.md +3 -0
- package/src/templates/base/NOTES.md +3 -0
- package/src/templates/base/README.md +3 -0
- package/src/templates/base/STATUS.md +13 -0
- package/src/templates/base/TODO.md +11 -0
- package/src/templates/overlays/coding/ARCHITECTURE.md +3 -0
- package/src/templates/overlays/coding/DEPLOY.md +3 -0
- package/src/templates/overlays/marketing/CAMPAIGNS.md +3 -0
- package/src/templates/overlays/marketing/METRICS.md +3 -0
- package/src/templates/overlays/research/FINDINGS.md +3 -0
- package/src/templates/overlays/research/SOURCES.md +3 -0
- package/src/tool.ts +282 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { readRegistry, withRegistry } from '../lib/registry.js';
|
|
4
|
+
import { checkAuthorization } from '../lib/auth.js';
|
|
5
|
+
import { topicKey, CAPSULE_VERSION, } from '../lib/types.js';
|
|
6
|
+
import { validateSlug, sanitizeSlug, jailCheck, rejectSymlink, htmlEscape, validateGroupId, validateThreadId, buildCallbackData, } from '../lib/security.js';
|
|
7
|
+
import { scaffoldCapsule } from '../lib/capsule.js';
|
|
8
|
+
import { buildTopicCard, buildInitSlugButtons, buildInitTypeButtons } from '../lib/telegram.js';
|
|
9
|
+
import { generateInclude } from '../lib/include-generator.js';
|
|
10
|
+
import { triggerRestart, getConfigWrites } from '../lib/config-restart.js';
|
|
11
|
+
import { appendAudit, buildAuditEntry } from '../lib/audit.js';
|
|
12
|
+
const VALID_TYPES = new Set(['coding', 'research', 'marketing', 'custom']);
|
|
13
|
+
export async function handleInit(ctx, args) {
|
|
14
|
+
const { workspaceDir, configDir, userId, groupId, threadId, rpc, logger, messageContext } = ctx;
|
|
15
|
+
if (!userId || !groupId || !threadId) {
|
|
16
|
+
return { text: 'Missing context: groupId, threadId, or userId not available. Run this command inside a Telegram forum topic.' };
|
|
17
|
+
}
|
|
18
|
+
// Validate IDs
|
|
19
|
+
if (!validateGroupId(groupId)) {
|
|
20
|
+
return { text: 'Invalid groupId format.' };
|
|
21
|
+
}
|
|
22
|
+
if (!validateThreadId(threadId)) {
|
|
23
|
+
return { text: 'Invalid threadId format.' };
|
|
24
|
+
}
|
|
25
|
+
const registry = readRegistry(workspaceDir);
|
|
26
|
+
// Auth check (user tier, with first-user bootstrap)
|
|
27
|
+
const auth = checkAuthorization(userId, 'init', registry);
|
|
28
|
+
if (!auth.authorized) {
|
|
29
|
+
return { text: auth.message ?? 'Not authorized.' };
|
|
30
|
+
}
|
|
31
|
+
// Max topics check
|
|
32
|
+
const topicCount = Object.keys(registry.topics).length;
|
|
33
|
+
if (topicCount >= registry.maxTopics) {
|
|
34
|
+
return {
|
|
35
|
+
text: `Maximum number of topics (${registry.maxTopics}) reached. Archive or remove existing topics first.`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// Check if topic already registered
|
|
39
|
+
const key = topicKey(groupId, threadId);
|
|
40
|
+
if (registry.topics[key]) {
|
|
41
|
+
return {
|
|
42
|
+
text: `This topic is already registered as <code>${htmlEscape(registry.topics[key].slug)}</code>.`,
|
|
43
|
+
parseMode: 'HTML',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Parse args: [slug] [type]
|
|
47
|
+
const parts = args.trim().split(/\s+/);
|
|
48
|
+
let slugArg = parts[0] ?? '';
|
|
49
|
+
let typeArg = parts[1] ?? '';
|
|
50
|
+
// Derive slug from topic title or args
|
|
51
|
+
const topicTitle = messageContext?.['topicTitle'] ?? '';
|
|
52
|
+
let slug;
|
|
53
|
+
if (slugArg) {
|
|
54
|
+
slug = slugArg;
|
|
55
|
+
}
|
|
56
|
+
else if (topicTitle) {
|
|
57
|
+
slug = sanitizeSlug(topicTitle);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
slug = `topic-${threadId}`;
|
|
61
|
+
}
|
|
62
|
+
// Ensure slug starts with a letter (sanitizeSlug may produce something starting with a digit)
|
|
63
|
+
if (slug && !/^[a-z]/.test(slug)) {
|
|
64
|
+
slug = 't-' + slug;
|
|
65
|
+
}
|
|
66
|
+
// Validate slug
|
|
67
|
+
if (!validateSlug(slug)) {
|
|
68
|
+
return {
|
|
69
|
+
text: `Invalid slug "${htmlEscape(slug)}". Must start with a letter, lowercase alphanumeric + hyphens, max 50 chars.`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// Determine type
|
|
73
|
+
let topicType = 'coding';
|
|
74
|
+
if (typeArg && VALID_TYPES.has(typeArg.toLowerCase())) {
|
|
75
|
+
topicType = typeArg.toLowerCase();
|
|
76
|
+
}
|
|
77
|
+
const projectsBase = path.join(workspaceDir, 'projects');
|
|
78
|
+
// Path jail check
|
|
79
|
+
if (!jailCheck(projectsBase, slug)) {
|
|
80
|
+
return { text: 'Path safety check failed. Slug may escape the projects directory.' };
|
|
81
|
+
}
|
|
82
|
+
// Symlink check on projects base
|
|
83
|
+
if (rejectSymlink(projectsBase)) {
|
|
84
|
+
return { text: 'Projects base is a symlink. Aborting for security.' };
|
|
85
|
+
}
|
|
86
|
+
// Collision detection (registry)
|
|
87
|
+
const slugInUse = Object.values(registry.topics).some((t) => t.slug === slug);
|
|
88
|
+
const diskExists = fs.existsSync(path.join(projectsBase, slug));
|
|
89
|
+
let finalSlug = slug;
|
|
90
|
+
if (slugInUse || diskExists) {
|
|
91
|
+
// Append last 4 chars of groupId for uniqueness
|
|
92
|
+
const suffix = groupId.replace(/^-/, '').slice(-4);
|
|
93
|
+
finalSlug = `${slug}-${suffix}`.slice(0, 50);
|
|
94
|
+
if (!validateSlug(finalSlug)) {
|
|
95
|
+
return { text: `Could not generate a unique slug. Please provide one: /topic init <slug> [type]` };
|
|
96
|
+
}
|
|
97
|
+
// Check the fallback slug too
|
|
98
|
+
const fallbackInUse = Object.values(registry.topics).some((t) => t.slug === finalSlug);
|
|
99
|
+
if (fallbackInUse) {
|
|
100
|
+
return {
|
|
101
|
+
text: `Both <code>${htmlEscape(slug)}</code> and <code>${htmlEscape(finalSlug)}</code> are taken. Please provide a unique slug: /topic init <slug> [type]`,
|
|
102
|
+
parseMode: 'HTML',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// Re-check jail for fallback slug
|
|
106
|
+
if (!jailCheck(projectsBase, finalSlug)) {
|
|
107
|
+
return { text: 'Path safety check failed for fallback slug.' };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Symlink check on target path
|
|
111
|
+
const targetPath = path.join(projectsBase, finalSlug);
|
|
112
|
+
if (rejectSymlink(targetPath)) {
|
|
113
|
+
return { text: 'Target path is a symlink. Aborting for security.' };
|
|
114
|
+
}
|
|
115
|
+
// Scaffold capsule and write registry entry atomically under lock
|
|
116
|
+
const isFirstUser = registry.topicManagerAdmins.length === 0;
|
|
117
|
+
try {
|
|
118
|
+
await withRegistry(workspaceDir, (data) => {
|
|
119
|
+
scaffoldCapsule(projectsBase, finalSlug, topicType);
|
|
120
|
+
const newEntry = {
|
|
121
|
+
groupId,
|
|
122
|
+
threadId,
|
|
123
|
+
slug: finalSlug,
|
|
124
|
+
type: topicType,
|
|
125
|
+
status: 'active',
|
|
126
|
+
capsuleVersion: CAPSULE_VERSION,
|
|
127
|
+
lastMessageAt: new Date().toISOString(),
|
|
128
|
+
lastDoctorReportAt: null,
|
|
129
|
+
lastDoctorRunAt: null,
|
|
130
|
+
snoozeUntil: null,
|
|
131
|
+
ignoreChecks: [],
|
|
132
|
+
consecutiveSilentDoctors: 0,
|
|
133
|
+
lastPostError: null,
|
|
134
|
+
extras: {},
|
|
135
|
+
};
|
|
136
|
+
data.topics[key] = newEntry;
|
|
137
|
+
// First-user bootstrap: add as admin
|
|
138
|
+
if (isFirstUser) {
|
|
139
|
+
data.topicManagerAdmins.push(userId);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
145
|
+
return { text: `Failed to initialize topic: ${htmlEscape(msg)}` };
|
|
146
|
+
}
|
|
147
|
+
// If configWrites enabled: regenerate include + trigger restart
|
|
148
|
+
let restartMsg = '';
|
|
149
|
+
const configWritesEnabled = await getConfigWrites(ctx.rpc);
|
|
150
|
+
if (configWritesEnabled) {
|
|
151
|
+
try {
|
|
152
|
+
const updatedRegistry = readRegistry(workspaceDir);
|
|
153
|
+
generateInclude(workspaceDir, updatedRegistry, configDir);
|
|
154
|
+
const result = await triggerRestart(rpc, logger);
|
|
155
|
+
if (!result.success && result.fallbackMessage) {
|
|
156
|
+
restartMsg = '\n' + result.fallbackMessage;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
161
|
+
restartMsg = `\nWarning: include generation failed: ${htmlEscape(msg)}`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Audit log
|
|
165
|
+
appendAudit(workspaceDir, buildAuditEntry(userId, 'init', finalSlug, `Initialized topic type=${topicType} group=${groupId} thread=${threadId}`));
|
|
166
|
+
// Build topic card
|
|
167
|
+
const topicCard = buildTopicCard(finalSlug, topicType, CAPSULE_VERSION);
|
|
168
|
+
let adminNote = '';
|
|
169
|
+
if (isFirstUser) {
|
|
170
|
+
adminNote = '\n\nYou are the first user and have been added as a telegram-manager admin.';
|
|
171
|
+
}
|
|
172
|
+
let slugNote = '';
|
|
173
|
+
if (finalSlug !== slug) {
|
|
174
|
+
slugNote = `\n\nNote: slug <code>${htmlEscape(slug)}</code> was taken, using <code>${htmlEscape(finalSlug)}</code> instead.`;
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
text: `${topicCard}${slugNote}${adminNote}${restartMsg}`,
|
|
178
|
+
parseMode: 'HTML',
|
|
179
|
+
pin: true,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
// ── Callback data byte limit ──────────────────────────────────────────
|
|
183
|
+
const CALLBACK_BYTE_LIMIT = 64;
|
|
184
|
+
/**
|
|
185
|
+
* Check whether a callback payload for the given slug fits within
|
|
186
|
+
* Telegram's 64-byte callback_data limit.
|
|
187
|
+
*/
|
|
188
|
+
function fitsCallbackLimit(action, slug, groupId, threadId, secret) {
|
|
189
|
+
const data = buildCallbackData(action, slug, groupId, threadId, secret);
|
|
190
|
+
return Buffer.byteLength(data, 'utf-8') <= CALLBACK_BYTE_LIMIT;
|
|
191
|
+
}
|
|
192
|
+
// ── Interactive init flow ─────────────────────────────────────────────
|
|
193
|
+
const INIT_TYPE_MAP = {
|
|
194
|
+
ic: 'coding',
|
|
195
|
+
ir: 'research',
|
|
196
|
+
im: 'marketing',
|
|
197
|
+
ix: 'custom',
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* Entry point for `/topic init`. If args are provided, delegates straight
|
|
201
|
+
* to `handleInit`. Otherwise starts the interactive two-step flow.
|
|
202
|
+
*/
|
|
203
|
+
export async function handleInitInteractive(ctx, args) {
|
|
204
|
+
if (args.trim()) {
|
|
205
|
+
return handleInit(ctx, args);
|
|
206
|
+
}
|
|
207
|
+
return buildSlugConfirmation(ctx);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Step 1: derive slug and present a [Confirm] inline button.
|
|
211
|
+
* Falls back to text instructions if the callback data would exceed 64 bytes.
|
|
212
|
+
*/
|
|
213
|
+
async function buildSlugConfirmation(ctx) {
|
|
214
|
+
const { workspaceDir, userId, groupId, threadId, messageContext } = ctx;
|
|
215
|
+
if (!userId || !groupId || !threadId) {
|
|
216
|
+
return { text: 'Missing context: groupId, threadId, or userId not available. Run this command inside a Telegram forum topic.' };
|
|
217
|
+
}
|
|
218
|
+
if (!validateGroupId(groupId)) {
|
|
219
|
+
return { text: 'Invalid groupId format.' };
|
|
220
|
+
}
|
|
221
|
+
if (!validateThreadId(threadId)) {
|
|
222
|
+
return { text: 'Invalid threadId format.' };
|
|
223
|
+
}
|
|
224
|
+
const registry = readRegistry(workspaceDir);
|
|
225
|
+
const auth = checkAuthorization(userId, 'init', registry);
|
|
226
|
+
if (!auth.authorized) {
|
|
227
|
+
return { text: auth.message ?? 'Not authorized.' };
|
|
228
|
+
}
|
|
229
|
+
const topicCount = Object.keys(registry.topics).length;
|
|
230
|
+
if (topicCount >= registry.maxTopics) {
|
|
231
|
+
return { text: `Maximum number of topics (${registry.maxTopics}) reached. Archive or remove existing topics first.` };
|
|
232
|
+
}
|
|
233
|
+
const key = topicKey(groupId, threadId);
|
|
234
|
+
if (registry.topics[key]) {
|
|
235
|
+
return {
|
|
236
|
+
text: `This topic is already registered as <code>${htmlEscape(registry.topics[key].slug)}</code>.`,
|
|
237
|
+
parseMode: 'HTML',
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
// Derive slug (same logic as handleInit lines 74–96)
|
|
241
|
+
const topicTitle = messageContext?.['topicTitle'] ?? '';
|
|
242
|
+
let slug;
|
|
243
|
+
if (topicTitle) {
|
|
244
|
+
slug = sanitizeSlug(topicTitle);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
slug = `topic-${threadId}`;
|
|
248
|
+
}
|
|
249
|
+
if (slug && !/^[a-z]/.test(slug)) {
|
|
250
|
+
slug = 't-' + slug;
|
|
251
|
+
}
|
|
252
|
+
if (!validateSlug(slug)) {
|
|
253
|
+
return {
|
|
254
|
+
text: `Invalid derived slug "${htmlEscape(slug)}". Please provide one: /topic init <slug> [type]`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
// Check callback byte limit — if slug is too long, fall back to text
|
|
258
|
+
if (!fitsCallbackLimit('is', slug, groupId, threadId, registry.callbackSecret)) {
|
|
259
|
+
return {
|
|
260
|
+
text: `Suggested slug: <code>${htmlEscape(slug)}</code>\n\nSlug is too long for inline buttons. Please run:\n<code>/topic init ${htmlEscape(slug)} [type]</code>`,
|
|
261
|
+
parseMode: 'HTML',
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const keyboard = buildInitSlugButtons(slug, groupId, threadId, registry.callbackSecret);
|
|
265
|
+
return {
|
|
266
|
+
text: `Initialize this topic as <code>${htmlEscape(slug)}</code>?`,
|
|
267
|
+
parseMode: 'HTML',
|
|
268
|
+
inlineKeyboard: keyboard,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Step 2 (callback `is`): re-validate, then show the type picker.
|
|
273
|
+
*/
|
|
274
|
+
export async function handleInitSlugConfirm(ctx, slug) {
|
|
275
|
+
const { workspaceDir, userId, groupId, threadId } = ctx;
|
|
276
|
+
if (!userId || !groupId || !threadId) {
|
|
277
|
+
return { text: 'Missing context.' };
|
|
278
|
+
}
|
|
279
|
+
const registry = readRegistry(workspaceDir);
|
|
280
|
+
const auth = checkAuthorization(userId, 'init', registry);
|
|
281
|
+
if (!auth.authorized) {
|
|
282
|
+
return { text: auth.message ?? 'Not authorized.' };
|
|
283
|
+
}
|
|
284
|
+
const key = topicKey(groupId, threadId);
|
|
285
|
+
if (registry.topics[key]) {
|
|
286
|
+
return {
|
|
287
|
+
text: `This topic is already registered as <code>${htmlEscape(registry.topics[key].slug)}</code>.`,
|
|
288
|
+
parseMode: 'HTML',
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
const keyboard = buildInitTypeButtons(slug, groupId, threadId, registry.callbackSecret);
|
|
292
|
+
return {
|
|
293
|
+
text: `Slug: <code>${htmlEscape(slug)}</code>\n\nPick a topic type:`,
|
|
294
|
+
parseMode: 'HTML',
|
|
295
|
+
inlineKeyboard: keyboard,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Step 3 (callbacks `ic`/`ir`/`im`/`ix`): complete init with chosen type.
|
|
300
|
+
*/
|
|
301
|
+
export async function handleInitTypeSelect(ctx, slug, type) {
|
|
302
|
+
return handleInit(ctx, `${slug} ${type}`);
|
|
303
|
+
}
|
|
304
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/D,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAsB5E"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readRegistry } from '../lib/registry.js';
|
|
2
|
+
import { checkAuthorization } from '../lib/auth.js';
|
|
3
|
+
import { buildListMessage } from '../lib/telegram.js';
|
|
4
|
+
export async function handleList(ctx) {
|
|
5
|
+
const { workspaceDir, userId } = ctx;
|
|
6
|
+
if (!userId) {
|
|
7
|
+
return { text: 'Missing context: userId not available.' };
|
|
8
|
+
}
|
|
9
|
+
const registry = readRegistry(workspaceDir);
|
|
10
|
+
// Auth check (admin tier)
|
|
11
|
+
const auth = checkAuthorization(userId, 'list', registry);
|
|
12
|
+
if (!auth.authorized) {
|
|
13
|
+
return { text: auth.message ?? 'Not authorized.' };
|
|
14
|
+
}
|
|
15
|
+
const topics = Object.values(registry.topics);
|
|
16
|
+
const text = buildListMessage(topics);
|
|
17
|
+
return {
|
|
18
|
+
text,
|
|
19
|
+
parseMode: 'HTML',
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAmB;IAClD,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IAErC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,IAAI,EAAE,wCAAwC,EAAE,CAAC;IAC5D,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAE5C,0BAA0B;IAC1B,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC1D,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,IAAI,iBAAiB,EAAE,CAAC;IACrD,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAEtC,OAAO;QACL,IAAI;QACJ,SAAS,EAAE,MAAM;KAClB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rename.d.ts","sourceRoot":"","sources":["../../src/commands/rename.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/D,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA8H/F"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { readRegistry, withRegistry } from '../lib/registry.js';
|
|
4
|
+
import { checkAuthorization } from '../lib/auth.js';
|
|
5
|
+
import { topicKey } from '../lib/types.js';
|
|
6
|
+
import { validateSlug, jailCheck, rejectSymlink, htmlEscape } from '../lib/security.js';
|
|
7
|
+
import { buildTopicCard } from '../lib/telegram.js';
|
|
8
|
+
import { generateInclude } from '../lib/include-generator.js';
|
|
9
|
+
import { triggerRestart, getConfigWrites } from '../lib/config-restart.js';
|
|
10
|
+
import { appendAudit, buildAuditEntry } from '../lib/audit.js';
|
|
11
|
+
export async function handleRename(ctx, newSlug) {
|
|
12
|
+
const { workspaceDir, configDir, userId, groupId, threadId, rpc, logger } = ctx;
|
|
13
|
+
if (!userId || !groupId || !threadId) {
|
|
14
|
+
return { text: 'Missing context: userId, groupId, or threadId not available.' };
|
|
15
|
+
}
|
|
16
|
+
const trimmedSlug = newSlug.trim();
|
|
17
|
+
if (!trimmedSlug) {
|
|
18
|
+
return { text: 'Usage: /topic rename <new-slug>' };
|
|
19
|
+
}
|
|
20
|
+
// Validate new slug
|
|
21
|
+
if (!validateSlug(trimmedSlug)) {
|
|
22
|
+
return {
|
|
23
|
+
text: `Invalid slug "${htmlEscape(trimmedSlug)}". Must match: lowercase letter start, alphanumeric + hyphens, max 50 chars.`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const registry = readRegistry(workspaceDir);
|
|
27
|
+
// Auth check (admin tier)
|
|
28
|
+
const auth = checkAuthorization(userId, 'rename', registry);
|
|
29
|
+
if (!auth.authorized) {
|
|
30
|
+
return { text: auth.message ?? 'Not authorized.' };
|
|
31
|
+
}
|
|
32
|
+
const key = topicKey(groupId, threadId);
|
|
33
|
+
const entry = registry.topics[key];
|
|
34
|
+
if (!entry) {
|
|
35
|
+
return { text: 'This topic is not registered. Run /topic init first.' };
|
|
36
|
+
}
|
|
37
|
+
const oldSlug = entry.slug;
|
|
38
|
+
if (oldSlug === trimmedSlug) {
|
|
39
|
+
return { text: `Topic is already named <code>${htmlEscape(oldSlug)}</code>.` };
|
|
40
|
+
}
|
|
41
|
+
const projectsBase = path.join(workspaceDir, 'projects');
|
|
42
|
+
// Path jail check for both old and new paths
|
|
43
|
+
if (!jailCheck(projectsBase, oldSlug)) {
|
|
44
|
+
return { text: 'Path safety check failed for current slug.' };
|
|
45
|
+
}
|
|
46
|
+
if (!jailCheck(projectsBase, trimmedSlug)) {
|
|
47
|
+
return { text: 'Path safety check failed for new slug.' };
|
|
48
|
+
}
|
|
49
|
+
const oldPath = path.join(projectsBase, oldSlug);
|
|
50
|
+
const newPath = path.join(projectsBase, trimmedSlug);
|
|
51
|
+
// Symlink check on both paths
|
|
52
|
+
if (rejectSymlink(oldPath)) {
|
|
53
|
+
return { text: 'Current capsule directory is a symlink. Aborting for security.' };
|
|
54
|
+
}
|
|
55
|
+
if (rejectSymlink(newPath)) {
|
|
56
|
+
return { text: 'Target capsule directory is a symlink. Aborting for security.' };
|
|
57
|
+
}
|
|
58
|
+
// Collision check: new slug in registry
|
|
59
|
+
const collisionInRegistry = Object.values(registry.topics).some((t) => t.slug === trimmedSlug);
|
|
60
|
+
if (collisionInRegistry) {
|
|
61
|
+
return {
|
|
62
|
+
text: `Slug <code>${htmlEscape(trimmedSlug)}</code> is already used by another topic.`,
|
|
63
|
+
parseMode: 'HTML',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Collision check: new path on disk
|
|
67
|
+
if (fs.existsSync(newPath)) {
|
|
68
|
+
return {
|
|
69
|
+
text: `Directory projects/${htmlEscape(trimmedSlug)}/ already exists on disk.`,
|
|
70
|
+
parseMode: 'HTML',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Rename folder on disk
|
|
74
|
+
if (!fs.existsSync(oldPath)) {
|
|
75
|
+
return { text: `Source capsule directory not found: projects/${htmlEscape(oldSlug)}/` };
|
|
76
|
+
}
|
|
77
|
+
// Rename folder and update registry atomically under lock
|
|
78
|
+
try {
|
|
79
|
+
await withRegistry(workspaceDir, (data) => {
|
|
80
|
+
const topic = data.topics[key];
|
|
81
|
+
if (topic) {
|
|
82
|
+
fs.renameSync(oldPath, newPath);
|
|
83
|
+
topic.slug = trimmedSlug;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
89
|
+
return { text: `Failed to rename capsule: ${htmlEscape(msg)}` };
|
|
90
|
+
}
|
|
91
|
+
// Regenerate include if configWrites enabled
|
|
92
|
+
let restartMsg = '';
|
|
93
|
+
const configWritesEnabled = await getConfigWrites(ctx.rpc);
|
|
94
|
+
if (configWritesEnabled) {
|
|
95
|
+
try {
|
|
96
|
+
const updatedRegistry = readRegistry(workspaceDir);
|
|
97
|
+
generateInclude(workspaceDir, updatedRegistry, configDir);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
101
|
+
restartMsg = `\nWarning: include generation failed: ${htmlEscape(msg)}`;
|
|
102
|
+
}
|
|
103
|
+
const result = await triggerRestart(rpc, logger);
|
|
104
|
+
if (!result.success && result.fallbackMessage) {
|
|
105
|
+
restartMsg += '\n' + result.fallbackMessage;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
appendAudit(workspaceDir, buildAuditEntry(userId, 'rename', trimmedSlug, `Renamed from ${oldSlug} to ${trimmedSlug}`));
|
|
109
|
+
const topicCard = buildTopicCard(trimmedSlug, entry.type, entry.capsuleVersion);
|
|
110
|
+
return {
|
|
111
|
+
text: `Topic renamed from <code>${htmlEscape(oldSlug)}</code> to <code>${htmlEscape(trimmedSlug)}</code>.\n\n${topicCard}${restartMsg}`,
|
|
112
|
+
parseMode: 'HTML',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=rename.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rename.js","sourceRoot":"","sources":["../../src/commands/rename.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snooze.d.ts","sourceRoot":"","sources":["../../src/commands/snooze.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAI/D,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA0D5F"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { withRegistry } from '../lib/registry.js';
|
|
2
|
+
import { readRegistry } from '../lib/registry.js';
|
|
3
|
+
import { checkAuthorization } from '../lib/auth.js';
|
|
4
|
+
import { topicKey } from '../lib/types.js';
|
|
5
|
+
import { htmlEscape } from '../lib/security.js';
|
|
6
|
+
import { appendAudit, buildAuditEntry } from '../lib/audit.js';
|
|
7
|
+
const DURATION_RE = /^(\d+)d$/;
|
|
8
|
+
export async function handleSnooze(ctx, args) {
|
|
9
|
+
const { workspaceDir, userId, groupId, threadId } = ctx;
|
|
10
|
+
if (!userId || !groupId || !threadId) {
|
|
11
|
+
return { text: 'Missing context: userId, groupId, or threadId not available.' };
|
|
12
|
+
}
|
|
13
|
+
// Parse duration
|
|
14
|
+
const trimmed = args.trim();
|
|
15
|
+
if (!trimmed) {
|
|
16
|
+
return { text: 'Usage: /topic snooze <Nd> (e.g., 7d, 30d)' };
|
|
17
|
+
}
|
|
18
|
+
const match = DURATION_RE.exec(trimmed);
|
|
19
|
+
if (!match) {
|
|
20
|
+
return { text: `Invalid duration "${htmlEscape(trimmed)}". Use format: 7d, 30d, etc.` };
|
|
21
|
+
}
|
|
22
|
+
const days = parseInt(match[1], 10);
|
|
23
|
+
if (days <= 0 || days > 365) {
|
|
24
|
+
return { text: 'Duration must be between 1 and 365 days.' };
|
|
25
|
+
}
|
|
26
|
+
const registry = readRegistry(workspaceDir);
|
|
27
|
+
// Auth check (user tier)
|
|
28
|
+
const auth = checkAuthorization(userId, 'snooze', registry);
|
|
29
|
+
if (!auth.authorized) {
|
|
30
|
+
return { text: auth.message ?? 'Not authorized.' };
|
|
31
|
+
}
|
|
32
|
+
const key = topicKey(groupId, threadId);
|
|
33
|
+
const entry = registry.topics[key];
|
|
34
|
+
if (!entry) {
|
|
35
|
+
return { text: 'This topic is not registered. Run /topic init first.' };
|
|
36
|
+
}
|
|
37
|
+
const snoozeUntil = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString();
|
|
38
|
+
await withRegistry(workspaceDir, (data) => {
|
|
39
|
+
const topic = data.topics[key];
|
|
40
|
+
if (topic) {
|
|
41
|
+
topic.snoozeUntil = snoozeUntil;
|
|
42
|
+
topic.consecutiveSilentDoctors = 0;
|
|
43
|
+
topic.status = 'snoozed';
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
appendAudit(workspaceDir, buildAuditEntry(userId, 'snooze', entry.slug, `Snoozed for ${days} days until ${snoozeUntil}`));
|
|
47
|
+
return {
|
|
48
|
+
text: `Topic <code>${htmlEscape(entry.slug)}</code> snoozed for ${days} days (until ${htmlEscape(snoozeUntil)}).`,
|
|
49
|
+
parseMode: 'HTML',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=snooze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snooze.js","sourceRoot":"","sources":["../../src/commands/snooze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/D,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAiD9E"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { readRegistry } from '../lib/registry.js';
|
|
4
|
+
import { checkAuthorization } from '../lib/auth.js';
|
|
5
|
+
import { topicKey } from '../lib/types.js';
|
|
6
|
+
import { jailCheck, rejectSymlink, htmlEscape } from '../lib/security.js';
|
|
7
|
+
import { truncateMessage } from '../lib/telegram.js';
|
|
8
|
+
export async function handleStatus(ctx) {
|
|
9
|
+
const { workspaceDir, userId, groupId, threadId } = ctx;
|
|
10
|
+
if (!userId || !groupId || !threadId) {
|
|
11
|
+
return { text: 'Missing context: userId, groupId, or threadId not available.' };
|
|
12
|
+
}
|
|
13
|
+
const registry = readRegistry(workspaceDir);
|
|
14
|
+
// Auth check (user tier)
|
|
15
|
+
const auth = checkAuthorization(userId, 'status', registry);
|
|
16
|
+
if (!auth.authorized) {
|
|
17
|
+
return { text: auth.message ?? 'Not authorized.' };
|
|
18
|
+
}
|
|
19
|
+
const key = topicKey(groupId, threadId);
|
|
20
|
+
const entry = registry.topics[key];
|
|
21
|
+
if (!entry) {
|
|
22
|
+
return { text: 'This topic is not registered. Run /topic init first.' };
|
|
23
|
+
}
|
|
24
|
+
const projectsBase = path.join(workspaceDir, 'projects');
|
|
25
|
+
const capsuleDir = path.join(projectsBase, entry.slug);
|
|
26
|
+
// Path safety
|
|
27
|
+
if (!jailCheck(projectsBase, entry.slug)) {
|
|
28
|
+
return { text: 'Path safety check failed.' };
|
|
29
|
+
}
|
|
30
|
+
if (rejectSymlink(capsuleDir)) {
|
|
31
|
+
return { text: 'Capsule directory is a symlink. Aborting for security.' };
|
|
32
|
+
}
|
|
33
|
+
const statusPath = path.join(capsuleDir, 'STATUS.md');
|
|
34
|
+
if (!fs.existsSync(statusPath)) {
|
|
35
|
+
return { text: 'STATUS.md not found in capsule. Run /topic doctor to diagnose.' };
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const content = fs.readFileSync(statusPath, 'utf-8');
|
|
39
|
+
return {
|
|
40
|
+
text: truncateMessage(content),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
45
|
+
return { text: `Failed to read STATUS.md: ${htmlEscape(msg)}` };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAmB;IACpD,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IAExD,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,8DAA8D,EAAE,CAAC;IAClF,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAE5C,yBAAyB;IACzB,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC5D,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,IAAI,iBAAiB,EAAE,CAAC;IACrD,CAAC;IAED,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAEnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,IAAI,EAAE,sDAAsD,EAAE,CAAC;IAC1E,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IACzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAEvD,cAAc;IACd,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,OAAO,EAAE,IAAI,EAAE,2BAA2B,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,wDAAwD,EAAE,CAAC;IAC5E,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAEtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,gEAAgE,EAAE,CAAC;IACpF,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACrD,OAAO;YACL,IAAI,EAAE,eAAe,CAAC,OAAO,CAAC;SAC/B,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,EAAE,IAAI,EAAE,6BAA6B,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;IAClE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/D,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAsC5E"}
|