@atollhq/cli 0.1.0 → 0.1.2
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 +34 -2
- package/dist/index.js +330 -107
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -19,6 +19,26 @@ atoll auth status
|
|
|
19
19
|
|
|
20
20
|
API keys are created in the Atoll web app under **Settings > Members > Add Agent** (for agents) or **Settings > Members > Create API Key** (for integrations).
|
|
21
21
|
|
|
22
|
+
### Multiple auth profiles
|
|
23
|
+
|
|
24
|
+
Use profiles when you have multiple agent keys with different org or project access:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
atoll auth login --profile agent-a --key sk_atoll_... --org acme
|
|
28
|
+
atoll auth login --profile agent-b --key sk_atoll_... --org client
|
|
29
|
+
atoll auth profiles
|
|
30
|
+
atoll auth use agent-a
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Run a single command with a specific profile:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
atoll --profile agent-b issue list
|
|
37
|
+
atoll --profile agent-a --org acme issue create --title "Fix login bug"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Each profile can store its own API key, default org, default team, and base URL. `--org` and `--team` still override the selected profile for a single command.
|
|
41
|
+
|
|
22
42
|
## Usage
|
|
23
43
|
|
|
24
44
|
```bash
|
|
@@ -34,14 +54,26 @@ Run `atoll --help` or `atoll <command> --help` for the full reference.
|
|
|
34
54
|
|
|
35
55
|
## Configuration
|
|
36
56
|
|
|
37
|
-
The CLI stores state in `~/.atoll/config.json`. You can override the org or team per-command:
|
|
57
|
+
The CLI stores state in `~/.atoll/config.json`. You can override the profile, org, or team per-command:
|
|
38
58
|
|
|
39
59
|
```bash
|
|
60
|
+
atoll --profile agent-a issue list
|
|
40
61
|
atoll --org my-org issue list
|
|
41
62
|
atoll --team eng issue create --title "..."
|
|
42
63
|
```
|
|
43
64
|
|
|
44
|
-
Or via env vars: `ATOLL_API_KEY`, `ATOLL_ORG`, `ATOLL_TEAM`.
|
|
65
|
+
Or via env vars: `ATOLL_PROFILE`, `ATOLL_API_KEY`, `ATOLL_ORG`, `ATOLL_TEAM`, `ATOLL_BASE_URL`.
|
|
66
|
+
|
|
67
|
+
## Output URLs
|
|
68
|
+
|
|
69
|
+
Issue and project JSON output includes a canonical `url` field pointing at the Atoll web app (honoring `ATOLL_BASE_URL` for self-hosted / local dev), suitable for pasting into standups, notifications, and follow-ups:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
atoll issue view ATOLL-42 | jq -r .url
|
|
73
|
+
# https://atollhq.com/acme/projects/atoll/issues/42
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`url` is `null` for orphan issues (no project) or missing identifiers, so consumers should handle that case.
|
|
45
77
|
|
|
46
78
|
## License
|
|
47
79
|
|
package/dist/index.js
CHANGED
|
@@ -14,8 +14,12 @@ var __export = (target, all) => {
|
|
|
14
14
|
var config_exports = {};
|
|
15
15
|
__export(config_exports, {
|
|
16
16
|
deleteConfig: () => deleteConfig,
|
|
17
|
+
ensureProfile: () => ensureProfile,
|
|
18
|
+
getActiveProfile: () => getActiveProfile,
|
|
17
19
|
getApiKey: () => getApiKey,
|
|
20
|
+
getProfile: () => getProfile,
|
|
18
21
|
readConfig: () => readConfig,
|
|
22
|
+
resolveConfig: () => resolveConfig,
|
|
19
23
|
writeConfig: () => writeConfig
|
|
20
24
|
});
|
|
21
25
|
function readConfig() {
|
|
@@ -37,8 +41,32 @@ function deleteConfig() {
|
|
|
37
41
|
(0, import_node_fs.unlinkSync)(CONFIG_PATH);
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
|
-
function
|
|
41
|
-
return process.env.
|
|
44
|
+
function getActiveProfile(config = readConfig()) {
|
|
45
|
+
return process.env.ATOLL_PROFILE || config.activeProfile;
|
|
46
|
+
}
|
|
47
|
+
function getProfile(config, profileName) {
|
|
48
|
+
if (!profileName) return void 0;
|
|
49
|
+
return config.profiles?.[profileName];
|
|
50
|
+
}
|
|
51
|
+
function ensureProfile(config, profileName) {
|
|
52
|
+
config.profiles ??= {};
|
|
53
|
+
config.profiles[profileName] ??= {};
|
|
54
|
+
return config.profiles[profileName];
|
|
55
|
+
}
|
|
56
|
+
function resolveConfig(opts) {
|
|
57
|
+
const config = readConfig();
|
|
58
|
+
const profileName = opts?.profile || getActiveProfile(config);
|
|
59
|
+
const profile = getProfile(config, profileName);
|
|
60
|
+
return {
|
|
61
|
+
profile: profileName,
|
|
62
|
+
apiKey: process.env.ATOLL_API_KEY || profile?.apiKey || (profileName ? void 0 : config.apiKey),
|
|
63
|
+
orgSlug: process.env.ATOLL_ORG || profile?.orgSlug || (profileName ? void 0 : config.orgSlug),
|
|
64
|
+
defaultTeam: process.env.ATOLL_TEAM || profile?.defaultTeam || (profileName ? void 0 : config.defaultTeam),
|
|
65
|
+
baseUrl: process.env.ATOLL_BASE_URL || profile?.baseUrl || (profileName ? void 0 : config.baseUrl)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function getApiKey(opts) {
|
|
69
|
+
return resolveConfig(opts).apiKey;
|
|
42
70
|
}
|
|
43
71
|
var import_node_fs, import_node_os, import_node_path, CONFIG_DIR, CONFIG_PATH;
|
|
44
72
|
var init_config = __esm({
|
|
@@ -86,10 +114,12 @@ function outputError(message) {
|
|
|
86
114
|
var DEFAULT_BASE_URL = "https://atollhq.com";
|
|
87
115
|
var AtollClient = class {
|
|
88
116
|
constructor(opts) {
|
|
89
|
-
|
|
90
|
-
|
|
117
|
+
const config = resolveConfig({ profile: opts?.profile });
|
|
118
|
+
this.baseUrl = opts?.baseUrl || config.baseUrl || DEFAULT_BASE_URL;
|
|
119
|
+
const key = opts?.apiKey || config.apiKey;
|
|
91
120
|
if (!key) {
|
|
92
|
-
|
|
121
|
+
const suffix = config.profile ? ` for profile "${config.profile}"` : "";
|
|
122
|
+
outputError(`No API key found${suffix}. Run \`atoll auth login --key <API_KEY>\` or set ATOLL_API_KEY.`);
|
|
93
123
|
process.exit(1);
|
|
94
124
|
}
|
|
95
125
|
this.apiKey = key;
|
|
@@ -132,28 +162,47 @@ var AtollClient = class {
|
|
|
132
162
|
|
|
133
163
|
// src/commands/auth.ts
|
|
134
164
|
var authCommand = new import_commander.Command("auth").description("Manage authentication");
|
|
135
|
-
authCommand.command("login").description("Save an API key to ~/.atoll/config.json").requiredOption("--key <API_KEY>", "API key to store").action((opts) => {
|
|
165
|
+
authCommand.command("login").description("Save an API key to ~/.atoll/config.json").requiredOption("--key <API_KEY>", "API key to store").option("--profile <name>", "Profile name to store this API key under").option("--org <slug>", "Default org slug for this profile").option("--team <team>", "Default team slug or ID for this profile").option("--base-url <url>", "Base URL for this profile").action((opts) => {
|
|
136
166
|
const config = readConfig();
|
|
137
|
-
|
|
167
|
+
const profileName = opts.profile || process.env.ATOLL_PROFILE || config.activeProfile;
|
|
168
|
+
const orgSlug = opts.org ?? process.env.ATOLL_CLI_ORG;
|
|
169
|
+
const defaultTeam = opts.team ?? process.env.ATOLL_CLI_TEAM;
|
|
170
|
+
const baseUrl = opts.baseUrl;
|
|
171
|
+
if (profileName) {
|
|
172
|
+
const profile = ensureProfile(config, profileName);
|
|
173
|
+
profile.apiKey = opts.key;
|
|
174
|
+
if (orgSlug !== void 0) profile.orgSlug = orgSlug;
|
|
175
|
+
if (defaultTeam !== void 0) profile.defaultTeam = defaultTeam;
|
|
176
|
+
if (baseUrl !== void 0) profile.baseUrl = baseUrl;
|
|
177
|
+
config.activeProfile = profileName;
|
|
178
|
+
} else {
|
|
179
|
+
config.apiKey = opts.key;
|
|
180
|
+
if (orgSlug !== void 0) config.orgSlug = orgSlug;
|
|
181
|
+
if (defaultTeam !== void 0) config.defaultTeam = defaultTeam;
|
|
182
|
+
if (baseUrl !== void 0) config.baseUrl = baseUrl;
|
|
183
|
+
}
|
|
138
184
|
writeConfig(config);
|
|
139
185
|
output(
|
|
140
|
-
{ status: "ok", message: "API key saved" },
|
|
141
|
-
"\u2713 API key saved to ~/.atoll/config.json"
|
|
186
|
+
{ status: "ok", message: "API key saved", profile: profileName ?? null },
|
|
187
|
+
profileName ? `\u2713 API key saved to profile "${profileName}" in ~/.atoll/config.json` : "\u2713 API key saved to ~/.atoll/config.json"
|
|
142
188
|
);
|
|
143
189
|
});
|
|
144
|
-
authCommand.command("status").description("Show current auth status and user info").action(async () => {
|
|
145
|
-
const
|
|
190
|
+
authCommand.command("status").description("Show current auth status and user info").option("--profile <name>", "Profile name to check").action(async (opts) => {
|
|
191
|
+
const resolved = resolveConfig({ profile: opts.profile });
|
|
192
|
+
const apiKey = resolved.apiKey;
|
|
146
193
|
if (!apiKey) {
|
|
147
|
-
|
|
194
|
+
const suffix = resolved.profile ? ` for profile "${resolved.profile}"` : "";
|
|
195
|
+
outputError(`Not authenticated${suffix}. Run \`atoll auth login --key <API_KEY>\` or set ATOLL_API_KEY.`);
|
|
148
196
|
process.exit(1);
|
|
149
197
|
}
|
|
150
198
|
try {
|
|
151
|
-
const client = new AtollClient({ apiKey });
|
|
199
|
+
const client = new AtollClient({ apiKey, baseUrl: resolved.baseUrl, profile: opts.profile });
|
|
152
200
|
const me = await client.get("/api/auth/me");
|
|
153
201
|
output(
|
|
154
202
|
{ status: "authenticated", ...me },
|
|
155
203
|
[
|
|
156
204
|
"\u2713 Authenticated",
|
|
205
|
+
resolved.profile ? ` Profile: ${resolved.profile}` : null,
|
|
157
206
|
me.user?.name ? ` User: ${me.user.name}` : null,
|
|
158
207
|
me.user?.email ? ` Email: ${me.user.email}` : null,
|
|
159
208
|
me.org?.name ? ` Org: ${me.org.name} (${me.org.slug})` : null
|
|
@@ -169,13 +218,67 @@ authCommand.command("status").description("Show current auth status and user inf
|
|
|
169
218
|
process.exit(1);
|
|
170
219
|
}
|
|
171
220
|
});
|
|
172
|
-
authCommand.command("
|
|
221
|
+
authCommand.command("profiles").description("List saved auth profiles").action(() => {
|
|
222
|
+
const config = readConfig();
|
|
223
|
+
const activeProfile = config.activeProfile;
|
|
224
|
+
const profiles = Object.entries(config.profiles ?? {}).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({
|
|
225
|
+
name,
|
|
226
|
+
active: name === activeProfile,
|
|
227
|
+
apiKeySet: !!profile.apiKey,
|
|
228
|
+
orgSlug: profile.orgSlug ?? null,
|
|
229
|
+
defaultTeam: profile.defaultTeam ?? null,
|
|
230
|
+
baseUrl: profile.baseUrl ?? null
|
|
231
|
+
}));
|
|
232
|
+
if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === "json") {
|
|
233
|
+
output({ activeProfile: activeProfile ?? null, profiles }, "");
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (profiles.length === 0) {
|
|
237
|
+
console.log("No profiles configured.");
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
for (const profile of profiles) {
|
|
241
|
+
const marker = profile.active ? "*" : " ";
|
|
242
|
+
const parts = [
|
|
243
|
+
`${marker} ${profile.name}`,
|
|
244
|
+
profile.apiKeySet ? "key=set" : "key=not set",
|
|
245
|
+
profile.orgSlug ? `org=${profile.orgSlug}` : null,
|
|
246
|
+
profile.defaultTeam ? `team=${profile.defaultTeam}` : null,
|
|
247
|
+
profile.baseUrl ? `baseUrl=${profile.baseUrl}` : null
|
|
248
|
+
].filter(Boolean);
|
|
249
|
+
console.log(parts.join(" "));
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
authCommand.command("use <profile>").description("Set the active auth profile").action((profileName) => {
|
|
173
253
|
const config = readConfig();
|
|
174
|
-
|
|
254
|
+
if (!getProfile(config, profileName)) {
|
|
255
|
+
outputError(`Profile "${profileName}" not found. Run \`atoll auth login --profile ${profileName} --key <API_KEY>\` first.`);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
config.activeProfile = profileName;
|
|
175
259
|
writeConfig(config);
|
|
176
260
|
output(
|
|
177
|
-
{
|
|
178
|
-
|
|
261
|
+
{ activeProfile: profileName },
|
|
262
|
+
`\u2713 Active profile set to "${profileName}"`
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
authCommand.command("logout").description("Remove stored API key").option("--profile <name>", "Profile name to log out").action((opts) => {
|
|
266
|
+
const config = readConfig();
|
|
267
|
+
const profileName = opts.profile || process.env.ATOLL_PROFILE || config.activeProfile;
|
|
268
|
+
if (profileName) {
|
|
269
|
+
const profile = getProfile(config, profileName);
|
|
270
|
+
if (!profile) {
|
|
271
|
+
outputError(`Profile "${profileName}" not found.`);
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
delete profile.apiKey;
|
|
275
|
+
} else {
|
|
276
|
+
delete config.apiKey;
|
|
277
|
+
}
|
|
278
|
+
writeConfig(config);
|
|
279
|
+
output(
|
|
280
|
+
{ status: "ok", message: "Logged out", profile: profileName ?? null },
|
|
281
|
+
profileName ? `\u2713 Logged out of profile "${profileName}" \u2014 API key removed (org/team config preserved)` : "\u2713 Logged out \u2014 API key removed (org/team config preserved)"
|
|
179
282
|
);
|
|
180
283
|
});
|
|
181
284
|
|
|
@@ -235,6 +338,30 @@ var RESET = isTTY() ? "\x1B[0m" : "";
|
|
|
235
338
|
var BOLD = isTTY() ? "\x1B[1m" : "";
|
|
236
339
|
var DIM = isTTY() ? "\x1B[2m" : "";
|
|
237
340
|
|
|
341
|
+
// src/lib/urls.ts
|
|
342
|
+
function derivePrefix(name) {
|
|
343
|
+
const cleaned = name.replace(/[^a-zA-Z0-9\s]/g, " ").trim();
|
|
344
|
+
const words = cleaned.split(/\s+/).filter(Boolean);
|
|
345
|
+
if (words.length === 0) return "TSK";
|
|
346
|
+
if (words.length >= 2) {
|
|
347
|
+
return words.slice(0, 5).map((w) => w[0]).join("").toUpperCase();
|
|
348
|
+
}
|
|
349
|
+
const word = words[0];
|
|
350
|
+
if (word.length <= 4) return word.toUpperCase();
|
|
351
|
+
return word.slice(0, 3).toUpperCase();
|
|
352
|
+
}
|
|
353
|
+
function stripTrailingSlash(baseUrl) {
|
|
354
|
+
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
355
|
+
}
|
|
356
|
+
function buildIssueUrl(baseUrl, orgSlug, projectSlug, issueNumber) {
|
|
357
|
+
if (!orgSlug || !projectSlug || issueNumber == null) return null;
|
|
358
|
+
return `${stripTrailingSlash(baseUrl)}/${orgSlug}/projects/${projectSlug}/issues/${issueNumber}`;
|
|
359
|
+
}
|
|
360
|
+
function buildProjectUrl(baseUrl, orgSlug, projectSlug) {
|
|
361
|
+
if (!orgSlug || !projectSlug) return null;
|
|
362
|
+
return `${stripTrailingSlash(baseUrl)}/${orgSlug}/projects/${projectSlug}`;
|
|
363
|
+
}
|
|
364
|
+
|
|
238
365
|
// src/commands/issue.ts
|
|
239
366
|
function handleApiError(err) {
|
|
240
367
|
const msg = err.message ?? String(err);
|
|
@@ -249,30 +376,48 @@ function handleApiError(err) {
|
|
|
249
376
|
}
|
|
250
377
|
var VALID_STATUSES = ["backlog", "todo", "in_progress", "done", "cancelled"];
|
|
251
378
|
var PRIORITY_LABELS = { 0: "Urgent", 1: "High", 2: "Medium", 3: "Low" };
|
|
252
|
-
async function
|
|
379
|
+
async function resolveOrg(client, orgSlugOverride) {
|
|
253
380
|
const { orgs } = await client.get("/api/orgs");
|
|
254
381
|
if (!orgs || orgs.length === 0) {
|
|
255
382
|
outputError("No organizations found. Create one first.");
|
|
256
383
|
process.exit(1);
|
|
257
384
|
}
|
|
258
|
-
if (orgs.length === 1) return orgs[0]
|
|
259
|
-
const {
|
|
260
|
-
const config =
|
|
261
|
-
const slug = orgSlugOverride ??
|
|
385
|
+
if (orgs.length === 1) return orgs[0];
|
|
386
|
+
const { resolveConfig: resolveConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
387
|
+
const config = resolveConfig2();
|
|
388
|
+
const slug = orgSlugOverride ?? config.orgSlug;
|
|
262
389
|
if (slug) {
|
|
263
390
|
const match = orgs.find((o) => o.slug === slug);
|
|
264
|
-
if (match) return match
|
|
391
|
+
if (match) return match;
|
|
265
392
|
outputError(`Org "${slug}" not found. Available: ${orgs.map((o) => o.slug).join(", ")}`);
|
|
266
393
|
process.exit(1);
|
|
267
394
|
}
|
|
268
395
|
outputError(`Multiple orgs found. Use --org <slug> or run \`atoll config set-org <slug>\`. Available: ${orgs.map((o) => o.slug).join(", ")}`);
|
|
269
396
|
process.exit(1);
|
|
270
397
|
}
|
|
398
|
+
async function resolveOrgId(client, orgSlugOverride) {
|
|
399
|
+
return (await resolveOrg(client, orgSlugOverride)).id;
|
|
400
|
+
}
|
|
401
|
+
async function fetchProjectMap(client, orgId) {
|
|
402
|
+
const { projects } = await client.get(
|
|
403
|
+
`/api/orgs/${orgId}/projects`
|
|
404
|
+
);
|
|
405
|
+
const map = /* @__PURE__ */ new Map();
|
|
406
|
+
for (const p of projects ?? []) map.set(p.id, p);
|
|
407
|
+
return map;
|
|
408
|
+
}
|
|
409
|
+
function attachIssueUrl(issue, baseUrl, orgSlug, projects) {
|
|
410
|
+
const project = issue.project_id ? projects.get(issue.project_id) : void 0;
|
|
411
|
+
return {
|
|
412
|
+
...issue,
|
|
413
|
+
url: buildIssueUrl(baseUrl, orgSlug, project?.slug, issue.number ?? null)
|
|
414
|
+
};
|
|
415
|
+
}
|
|
271
416
|
async function resolveIssueId(client, orgId, identifier) {
|
|
272
417
|
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identifier)) {
|
|
273
418
|
return identifier;
|
|
274
419
|
}
|
|
275
|
-
const match = identifier.match(/^(?:[A-Za-
|
|
420
|
+
const match = identifier.match(/^(?:[A-Za-z0-9]+-)?(\d+)$/);
|
|
276
421
|
if (!match) {
|
|
277
422
|
outputError(`Invalid identifier: "${identifier}". Use a UUID or PREFIX-NUMBER (e.g. ATOLL-42).`);
|
|
278
423
|
process.exit(2);
|
|
@@ -298,8 +443,13 @@ async function resolveIssueId(client, orgId, identifier) {
|
|
|
298
443
|
outputError(`Issue #${num} not found.`);
|
|
299
444
|
process.exit(1);
|
|
300
445
|
}
|
|
301
|
-
function formatIdentifier(issue) {
|
|
302
|
-
|
|
446
|
+
function formatIdentifier(issue, projects) {
|
|
447
|
+
if (issue.number == null) return issue.id.slice(0, 8);
|
|
448
|
+
if (projects && issue.project_id) {
|
|
449
|
+
const project = projects.get(issue.project_id);
|
|
450
|
+
if (project?.name) return `${derivePrefix(project.name)}-${issue.number}`;
|
|
451
|
+
}
|
|
452
|
+
return `ATOLL-${issue.number}`;
|
|
303
453
|
}
|
|
304
454
|
function padEnd(str, len) {
|
|
305
455
|
return str.length >= len ? str.slice(0, len) : str + " ".repeat(len - str.length);
|
|
@@ -315,35 +465,41 @@ Examples:
|
|
|
315
465
|
issueCommand.command("list").description("List issues").option("--status <status>", `Filter by status (${VALID_STATUSES.join(", ")})`).option("--assignee <user>", "Filter by assignee ID").option("--priority <n>", "Filter by priority (0=urgent, 1=high, 2=medium, 3=low)", parseInt).option("--limit <n>", "Max results (1-100)", parseInt).action(async (opts) => {
|
|
316
466
|
try {
|
|
317
467
|
const client = new AtollClient();
|
|
318
|
-
const
|
|
468
|
+
const org = await resolveOrg(client);
|
|
319
469
|
const params = new URLSearchParams();
|
|
320
470
|
if (opts.status) params.set("status", opts.status);
|
|
321
471
|
if (opts.assignee) params.set("assigneeId", opts.assignee);
|
|
322
472
|
if (opts.priority !== void 0) params.set("priority", String(opts.priority));
|
|
323
473
|
if (opts.limit !== void 0) params.set("limit", String(opts.limit));
|
|
324
474
|
const qs = params.toString();
|
|
325
|
-
const data = await
|
|
475
|
+
const [data, projects] = await Promise.all([
|
|
476
|
+
client.get(`/api/orgs/${org.id}/issues${qs ? `?${qs}` : ""}`),
|
|
477
|
+
fetchProjectMap(client, org.id)
|
|
478
|
+
]);
|
|
479
|
+
const enriched = data.issues.map(
|
|
480
|
+
(issue) => attachIssueUrl(issue, client.baseUrl, org.slug, projects)
|
|
481
|
+
);
|
|
326
482
|
if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === "json") {
|
|
327
|
-
for (const issue of
|
|
483
|
+
for (const issue of enriched) {
|
|
328
484
|
process.stdout.write(JSON.stringify(issue) + "\n");
|
|
329
485
|
}
|
|
330
486
|
return;
|
|
331
487
|
}
|
|
332
|
-
if (
|
|
488
|
+
if (enriched.length === 0) {
|
|
333
489
|
console.log("No issues found.");
|
|
334
490
|
return;
|
|
335
491
|
}
|
|
336
492
|
const header = bold(`${padEnd("ID", 10)} ${padEnd("STATUS", 14)} ${padEnd("PRI", 8)} TITLE`);
|
|
337
493
|
console.log(header);
|
|
338
494
|
console.log("\u2500".repeat(82));
|
|
339
|
-
for (const issue of
|
|
340
|
-
const id = formatIdentifier(issue);
|
|
495
|
+
for (const issue of enriched) {
|
|
496
|
+
const id = formatIdentifier(issue, projects);
|
|
341
497
|
const pri = PRIORITY_LABELS[issue.priority] ?? String(issue.priority);
|
|
342
498
|
const title = issue.title.length > 50 ? issue.title.slice(0, 47) + "\u2026" : issue.title;
|
|
343
499
|
const icon = priorityIcon(issue.priority);
|
|
344
500
|
console.log(`${padEnd(id, 10)} ${statusColor(issue.status, padEnd(issue.status, 14))} ${icon} ${padEnd(pri, 8)} ${title}`);
|
|
345
501
|
}
|
|
346
|
-
console.log(dim(`${
|
|
502
|
+
console.log(dim(`${enriched.length} of ${data.total} issues`));
|
|
347
503
|
} catch (err) {
|
|
348
504
|
handleApiError(err);
|
|
349
505
|
}
|
|
@@ -351,33 +507,38 @@ issueCommand.command("list").description("List issues").option("--status <status
|
|
|
351
507
|
issueCommand.command("view <identifier>").description("View issue details (UUID or PREFIX-NUMBER e.g. ATOLL-42)").action(async (identifier) => {
|
|
352
508
|
try {
|
|
353
509
|
const client = new AtollClient();
|
|
354
|
-
const
|
|
355
|
-
const issueId = await resolveIssueId(client,
|
|
356
|
-
const { issue } = await
|
|
510
|
+
const org = await resolveOrg(client);
|
|
511
|
+
const issueId = await resolveIssueId(client, org.id, identifier);
|
|
512
|
+
const [{ issue }, projects] = await Promise.all([
|
|
513
|
+
client.get(`/api/orgs/${org.id}/issues/${issueId}`),
|
|
514
|
+
fetchProjectMap(client, org.id)
|
|
515
|
+
]);
|
|
516
|
+
const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);
|
|
357
517
|
if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === "json") {
|
|
358
|
-
process.stdout.write(JSON.stringify(
|
|
518
|
+
process.stdout.write(JSON.stringify(enriched) + "\n");
|
|
359
519
|
return;
|
|
360
520
|
}
|
|
361
|
-
const id = formatIdentifier(
|
|
362
|
-
const pri = PRIORITY_LABELS[
|
|
363
|
-
console.log(`${bold(id)} ${
|
|
364
|
-
console.log(`Status: ${statusColor(
|
|
365
|
-
if (
|
|
366
|
-
if (
|
|
367
|
-
if (
|
|
521
|
+
const id = formatIdentifier(enriched, projects);
|
|
522
|
+
const pri = PRIORITY_LABELS[enriched.priority] ?? String(enriched.priority);
|
|
523
|
+
console.log(`${bold(id)} ${enriched.title}`);
|
|
524
|
+
console.log(`Status: ${statusColor(enriched.status, enriched.status)} Priority: ${priorityIcon(enriched.priority)} ${pri}`);
|
|
525
|
+
if (enriched.url) console.log(`URL: ${enriched.url}`);
|
|
526
|
+
if (enriched.assignee_id) console.log(`Assignee: ${enriched.assignee_id}`);
|
|
527
|
+
if (enriched.due_date) console.log(`Due: ${enriched.due_date}`);
|
|
528
|
+
if (enriched.description) {
|
|
368
529
|
console.log(`
|
|
369
|
-
${
|
|
530
|
+
${enriched.description}`);
|
|
370
531
|
}
|
|
371
|
-
if (
|
|
372
|
-
const labels =
|
|
532
|
+
if (enriched.issue_labels && enriched.issue_labels.length > 0) {
|
|
533
|
+
const labels = enriched.issue_labels.map((l) => l.labels.name).join(", ");
|
|
373
534
|
console.log(`Labels: ${labels}`);
|
|
374
535
|
}
|
|
375
|
-
if (
|
|
536
|
+
if (enriched.sub_tasks && Array.isArray(enriched.sub_tasks) && enriched.sub_tasks.length > 0) {
|
|
376
537
|
console.log(`
|
|
377
|
-
Subtasks: ${
|
|
538
|
+
Subtasks: ${enriched.sub_tasks.length}`);
|
|
378
539
|
}
|
|
379
|
-
if (
|
|
380
|
-
if (
|
|
540
|
+
if (enriched.created_at) console.log(dim(`Created: ${enriched.created_at}`));
|
|
541
|
+
if (enriched.updated_at) console.log(dim(`Updated: ${enriched.updated_at}`));
|
|
381
542
|
} catch (err) {
|
|
382
543
|
handleApiError(err);
|
|
383
544
|
}
|
|
@@ -393,15 +554,18 @@ issueCommand.command("create").description("Create a new issue").requiredOption(
|
|
|
393
554
|
process.exit(2);
|
|
394
555
|
}
|
|
395
556
|
const client = new AtollClient();
|
|
396
|
-
const
|
|
557
|
+
const org = await resolveOrg(client);
|
|
397
558
|
const body = { title: opts.title };
|
|
398
559
|
if (opts.description !== void 0) body.description = opts.description;
|
|
399
560
|
if (opts.status) body.status = opts.status;
|
|
400
561
|
if (opts.priority !== void 0) body.priority = opts.priority;
|
|
401
|
-
const { issue } = await client.post(`/api/orgs/${
|
|
562
|
+
const { issue } = await client.post(`/api/orgs/${org.id}/issues`, body);
|
|
563
|
+
const projects = await fetchProjectMap(client, org.id);
|
|
564
|
+
const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);
|
|
402
565
|
output(
|
|
403
|
-
{ issue },
|
|
404
|
-
success(`Created ${formatIdentifier(
|
|
566
|
+
{ issue: enriched },
|
|
567
|
+
success(`Created ${formatIdentifier(enriched, projects)}: ${enriched.title}${enriched.url ? `
|
|
568
|
+
${enriched.url}` : ""}`)
|
|
405
569
|
);
|
|
406
570
|
} catch (err) {
|
|
407
571
|
handleApiError(err);
|
|
@@ -426,12 +590,14 @@ issueCommand.command("update <identifier>").description("Update an issue").optio
|
|
|
426
590
|
process.exit(2);
|
|
427
591
|
}
|
|
428
592
|
const client = new AtollClient();
|
|
429
|
-
const
|
|
430
|
-
const issueId = await resolveIssueId(client,
|
|
431
|
-
const { issue } = await client.patch(`/api/orgs/${
|
|
593
|
+
const org = await resolveOrg(client);
|
|
594
|
+
const issueId = await resolveIssueId(client, org.id, identifier);
|
|
595
|
+
const { issue } = await client.patch(`/api/orgs/${org.id}/issues/${issueId}`, body);
|
|
596
|
+
const projects = await fetchProjectMap(client, org.id);
|
|
597
|
+
const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);
|
|
432
598
|
output(
|
|
433
|
-
{ issue },
|
|
434
|
-
success(`Updated ${formatIdentifier(
|
|
599
|
+
{ issue: enriched },
|
|
600
|
+
success(`Updated ${formatIdentifier(enriched, projects)}: ${enriched.title}`)
|
|
435
601
|
);
|
|
436
602
|
} catch (err) {
|
|
437
603
|
handleApiError(err);
|
|
@@ -454,8 +620,8 @@ issueCommand.command("delete <identifier>").description("Delete an issue (admin/
|
|
|
454
620
|
issueCommand.command("assign <identifier>").description("Assign an issue to a user or agent").requiredOption("--to <user>", 'User/agent ID or "self" for yourself').action(async (identifier, opts) => {
|
|
455
621
|
try {
|
|
456
622
|
const client = new AtollClient();
|
|
457
|
-
const
|
|
458
|
-
const issueId = await resolveIssueId(client,
|
|
623
|
+
const org = await resolveOrg(client);
|
|
624
|
+
const issueId = await resolveIssueId(client, org.id, identifier);
|
|
459
625
|
let assigneeId = opts.to;
|
|
460
626
|
if (assigneeId === "self") {
|
|
461
627
|
const me = await client.get("/api/auth/me");
|
|
@@ -465,7 +631,7 @@ issueCommand.command("assign <identifier>").description("Assign an issue to a us
|
|
|
465
631
|
process.exit(1);
|
|
466
632
|
}
|
|
467
633
|
const { members } = await client.get(
|
|
468
|
-
`/api/orgs/${
|
|
634
|
+
`/api/orgs/${org.id}/members`
|
|
469
635
|
);
|
|
470
636
|
const member = members.find((m) => m.id === callerUserId || m.user_id === callerUserId);
|
|
471
637
|
if (!member) {
|
|
@@ -475,12 +641,14 @@ issueCommand.command("assign <identifier>").description("Assign an issue to a us
|
|
|
475
641
|
assigneeId = member.id;
|
|
476
642
|
}
|
|
477
643
|
const { issue } = await client.patch(
|
|
478
|
-
`/api/orgs/${
|
|
644
|
+
`/api/orgs/${org.id}/issues/${issueId}`,
|
|
479
645
|
{ assignee_id: assigneeId }
|
|
480
646
|
);
|
|
647
|
+
const projects = await fetchProjectMap(client, org.id);
|
|
648
|
+
const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);
|
|
481
649
|
output(
|
|
482
|
-
{ issue },
|
|
483
|
-
success(`Assigned ${formatIdentifier(
|
|
650
|
+
{ issue: enriched },
|
|
651
|
+
success(`Assigned ${formatIdentifier(enriched, projects)} to ${opts.to}`)
|
|
484
652
|
);
|
|
485
653
|
} catch (err) {
|
|
486
654
|
handleApiError(err);
|
|
@@ -489,15 +657,17 @@ issueCommand.command("assign <identifier>").description("Assign an issue to a us
|
|
|
489
657
|
issueCommand.command("unassign <identifier>").description("Remove assignee from an issue").action(async (identifier) => {
|
|
490
658
|
try {
|
|
491
659
|
const client = new AtollClient();
|
|
492
|
-
const
|
|
493
|
-
const issueId = await resolveIssueId(client,
|
|
660
|
+
const org = await resolveOrg(client);
|
|
661
|
+
const issueId = await resolveIssueId(client, org.id, identifier);
|
|
494
662
|
const { issue } = await client.patch(
|
|
495
|
-
`/api/orgs/${
|
|
663
|
+
`/api/orgs/${org.id}/issues/${issueId}`,
|
|
496
664
|
{ assignee_id: null }
|
|
497
665
|
);
|
|
666
|
+
const projects = await fetchProjectMap(client, org.id);
|
|
667
|
+
const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);
|
|
498
668
|
output(
|
|
499
|
-
{ issue },
|
|
500
|
-
success(`Unassigned ${formatIdentifier(
|
|
669
|
+
{ issue: enriched },
|
|
670
|
+
success(`Unassigned ${formatIdentifier(enriched, projects)}`)
|
|
501
671
|
);
|
|
502
672
|
} catch (err) {
|
|
503
673
|
handleApiError(err);
|
|
@@ -602,6 +772,9 @@ commentCommand.command("delete <comment-id>").description("Delete a comment").re
|
|
|
602
772
|
|
|
603
773
|
// src/commands/project.ts
|
|
604
774
|
var import_commander4 = require("commander");
|
|
775
|
+
function attachProjectUrl(project, baseUrl, orgSlug) {
|
|
776
|
+
return { ...project, url: buildProjectUrl(baseUrl, orgSlug, project.slug) };
|
|
777
|
+
}
|
|
605
778
|
function progressBar(progress, width = 20) {
|
|
606
779
|
const filled = Math.round(progress / 100 * width);
|
|
607
780
|
const empty = width - filled;
|
|
@@ -615,9 +788,9 @@ Examples:
|
|
|
615
788
|
projectCommand.command("list").description("List all projects with progress").action(async () => {
|
|
616
789
|
const client = new AtollClient();
|
|
617
790
|
try {
|
|
618
|
-
const
|
|
619
|
-
const data = await client.get(`/api/orgs/${
|
|
620
|
-
const projects = data.projects ?? [];
|
|
791
|
+
const org = await resolveOrg(client);
|
|
792
|
+
const data = await client.get(`/api/orgs/${org.id}/projects`);
|
|
793
|
+
const projects = (data.projects ?? []).map((p) => attachProjectUrl(p, client.baseUrl, org.slug));
|
|
621
794
|
if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === "json") {
|
|
622
795
|
for (const p of projects) {
|
|
623
796
|
process.stdout.write(JSON.stringify(p) + "\n");
|
|
@@ -634,6 +807,7 @@ projectCommand.command("list").description("List all projects with progress").ac
|
|
|
634
807
|
console.log(`${bold(`${p.icon} ${p.name}`)} ${dim(`(${p.id})`)}`);
|
|
635
808
|
if (p.description) console.log(` ${dim(p.description)}`);
|
|
636
809
|
console.log(` ${cyan(bar)} ${p.issueCount ?? 0} issues, ${p.completedCount ?? 0} done`);
|
|
810
|
+
if (p.url) console.log(` ${dim(p.url)}`);
|
|
637
811
|
if (p.status === "archived") console.log(` ${dim("[archived]")}`);
|
|
638
812
|
console.log("");
|
|
639
813
|
}
|
|
@@ -644,16 +818,18 @@ projectCommand.command("list").description("List all projects with progress").ac
|
|
|
644
818
|
projectCommand.command("create").description("Create a new project").requiredOption("--name <name>", "Project name").option("--description <desc>", "Project description").option("--color <color>", "Project color (hex)", "#6366f1").option("--icon <icon>", "Project icon (emoji)", "\u{1F4C1}").action(async (opts) => {
|
|
645
819
|
const client = new AtollClient();
|
|
646
820
|
try {
|
|
647
|
-
const
|
|
648
|
-
const data = await client.post(`/api/orgs/${
|
|
821
|
+
const org = await resolveOrg(client);
|
|
822
|
+
const data = await client.post(`/api/orgs/${org.id}/projects`, {
|
|
649
823
|
name: opts.name,
|
|
650
824
|
description: opts.description ?? null,
|
|
651
825
|
color: opts.color,
|
|
652
826
|
icon: opts.icon
|
|
653
827
|
});
|
|
828
|
+
const project = attachProjectUrl(data.project, client.baseUrl, org.slug);
|
|
654
829
|
output(
|
|
655
|
-
{ project
|
|
656
|
-
success(`Created project ${
|
|
830
|
+
{ project },
|
|
831
|
+
success(`Created project ${project.icon} ${project.name} (${dim(project.id)})${project.url ? `
|
|
832
|
+
${project.url}` : ""}`)
|
|
657
833
|
);
|
|
658
834
|
} catch (err) {
|
|
659
835
|
handleApiError(err);
|
|
@@ -662,11 +838,11 @@ projectCommand.command("create").description("Create a new project").requiredOpt
|
|
|
662
838
|
projectCommand.command("view <projectId>").description("View project details and issues").action(async (projectId) => {
|
|
663
839
|
const client = new AtollClient();
|
|
664
840
|
try {
|
|
665
|
-
const
|
|
841
|
+
const org = await resolveOrg(client);
|
|
666
842
|
const data = await client.get(
|
|
667
|
-
`/api/orgs/${
|
|
843
|
+
`/api/orgs/${org.id}/projects/${projectId}`
|
|
668
844
|
);
|
|
669
|
-
const p = data.project;
|
|
845
|
+
const p = attachProjectUrl(data.project, client.baseUrl, org.slug);
|
|
670
846
|
if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === "json") {
|
|
671
847
|
process.stdout.write(JSON.stringify(p) + "\n");
|
|
672
848
|
return;
|
|
@@ -674,6 +850,7 @@ projectCommand.command("view <projectId>").description("View project details and
|
|
|
674
850
|
const progress = p.progress ?? 0;
|
|
675
851
|
console.log(bold(`${p.icon} ${p.name}`));
|
|
676
852
|
if (p.description) console.log(` ${p.description}`);
|
|
853
|
+
if (p.url) console.log(` ${dim(p.url)}`);
|
|
677
854
|
console.log(` ${cyan(progressBar(progress))}`);
|
|
678
855
|
console.log(` ${p.issueCount ?? 0} issues \xB7 ${p.completedCount ?? 0} completed`);
|
|
679
856
|
console.log("");
|
|
@@ -767,42 +944,76 @@ var configCommand = new import_commander6.Command("config").description("Manage
|
|
|
767
944
|
Examples:
|
|
768
945
|
$ atoll config show
|
|
769
946
|
$ atoll config set-org my-org
|
|
770
|
-
$ atoll config set-team team-abc123
|
|
771
|
-
|
|
947
|
+
$ atoll config set-team team-abc123
|
|
948
|
+
$ atoll config set-base-url https://atollhq.com`);
|
|
949
|
+
configCommand.command("show").description("Display current configuration").option("--profile <name>", "Profile name to show").action((opts) => {
|
|
772
950
|
const cfg = readConfig();
|
|
773
|
-
const
|
|
951
|
+
const activeProfile = cfg.activeProfile;
|
|
952
|
+
const resolved = resolveConfig({ profile: opts.profile });
|
|
953
|
+
const apiKeySet = !!resolved.apiKey;
|
|
774
954
|
if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === "json") {
|
|
775
955
|
output(
|
|
776
956
|
{
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
957
|
+
activeProfile: activeProfile ?? null,
|
|
958
|
+
selectedProfile: resolved.profile ?? null,
|
|
959
|
+
orgSlug: resolved.orgSlug ?? null,
|
|
960
|
+
defaultTeam: resolved.defaultTeam ?? null,
|
|
961
|
+
baseUrl: resolved.baseUrl ?? null,
|
|
962
|
+
apiKeySet,
|
|
963
|
+
profiles: Object.keys(cfg.profiles ?? {}).sort()
|
|
780
964
|
},
|
|
781
965
|
""
|
|
782
966
|
);
|
|
783
967
|
return;
|
|
784
968
|
}
|
|
785
969
|
console.log(`${bold("Atoll CLI Configuration")}`);
|
|
786
|
-
console.log(` ${dim("
|
|
787
|
-
console.log(` ${dim("
|
|
970
|
+
console.log(` ${dim("Active profile:")} ${activeProfile ? bold(activeProfile) : gray("(not set)")}`);
|
|
971
|
+
console.log(` ${dim("Selected profile:")} ${resolved.profile ? bold(resolved.profile) : gray("(legacy config)")}`);
|
|
972
|
+
console.log(` ${dim("Org slug:")} ${resolved.orgSlug ? bold(resolved.orgSlug) : gray("(not set)")}`);
|
|
973
|
+
console.log(` ${dim("Default team:")} ${resolved.defaultTeam ? bold(resolved.defaultTeam) : gray("(not set)")}`);
|
|
974
|
+
console.log(` ${dim("Base URL:")} ${resolved.baseUrl ? bold(resolved.baseUrl) : gray("(default)")}`);
|
|
788
975
|
console.log(` ${dim("API key:")} ${apiKeySet ? bold("set") : gray("not set \u2014 run `atoll auth login`")}`);
|
|
789
976
|
});
|
|
790
|
-
configCommand.command("set-org <slug>").description("Set the default organisation slug").action((slug) => {
|
|
977
|
+
configCommand.command("set-org <slug>").description("Set the default organisation slug").option("--profile <name>", "Profile name to update").action((slug, opts) => {
|
|
791
978
|
const cfg = readConfig();
|
|
792
|
-
|
|
979
|
+
const profileName = opts.profile || process.env.ATOLL_PROFILE || cfg.activeProfile;
|
|
980
|
+
if (profileName) {
|
|
981
|
+
ensureProfile(cfg, profileName).orgSlug = slug;
|
|
982
|
+
} else {
|
|
983
|
+
cfg.orgSlug = slug;
|
|
984
|
+
}
|
|
793
985
|
writeConfig(cfg);
|
|
794
986
|
output(
|
|
795
|
-
{ orgSlug: slug },
|
|
796
|
-
success(`Default org set to "${slug}"`)
|
|
987
|
+
{ orgSlug: slug, profile: profileName ?? null },
|
|
988
|
+
profileName ? success(`Default org for profile "${profileName}" set to "${slug}"`) : success(`Default org set to "${slug}"`)
|
|
797
989
|
);
|
|
798
990
|
});
|
|
799
|
-
configCommand.command("set-team <team>").description("Set the default team slug or ID").action((team) => {
|
|
991
|
+
configCommand.command("set-team <team>").description("Set the default team slug or ID").option("--profile <name>", "Profile name to update").action((team, opts) => {
|
|
800
992
|
const cfg = readConfig();
|
|
801
|
-
|
|
993
|
+
const profileName = opts.profile || process.env.ATOLL_PROFILE || cfg.activeProfile;
|
|
994
|
+
if (profileName) {
|
|
995
|
+
ensureProfile(cfg, profileName).defaultTeam = team;
|
|
996
|
+
} else {
|
|
997
|
+
cfg.defaultTeam = team;
|
|
998
|
+
}
|
|
802
999
|
writeConfig(cfg);
|
|
803
1000
|
output(
|
|
804
|
-
{ defaultTeam: team },
|
|
805
|
-
success(`Default team set to "${team}"`)
|
|
1001
|
+
{ defaultTeam: team, profile: profileName ?? null },
|
|
1002
|
+
profileName ? success(`Default team for profile "${profileName}" set to "${team}"`) : success(`Default team set to "${team}"`)
|
|
1003
|
+
);
|
|
1004
|
+
});
|
|
1005
|
+
configCommand.command("set-base-url <url>").description("Set the default API base URL").option("--profile <name>", "Profile name to update").action((url, opts) => {
|
|
1006
|
+
const cfg = readConfig();
|
|
1007
|
+
const profileName = opts.profile || process.env.ATOLL_PROFILE || cfg.activeProfile;
|
|
1008
|
+
if (profileName) {
|
|
1009
|
+
ensureProfile(cfg, profileName).baseUrl = url;
|
|
1010
|
+
} else {
|
|
1011
|
+
cfg.baseUrl = url;
|
|
1012
|
+
}
|
|
1013
|
+
writeConfig(cfg);
|
|
1014
|
+
output(
|
|
1015
|
+
{ baseUrl: url, profile: profileName ?? null },
|
|
1016
|
+
profileName ? success(`Base URL for profile "${profileName}" set to "${url}"`) : success(`Base URL set to "${url}"`)
|
|
806
1017
|
);
|
|
807
1018
|
});
|
|
808
1019
|
|
|
@@ -826,9 +1037,9 @@ async function resolveOrgId2(client, orgSlugOverride) {
|
|
|
826
1037
|
process.exit(1);
|
|
827
1038
|
}
|
|
828
1039
|
if (orgs.length === 1) return orgs[0].id;
|
|
829
|
-
const {
|
|
830
|
-
const config =
|
|
831
|
-
const slug = orgSlugOverride ??
|
|
1040
|
+
const { resolveConfig: resolveConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
1041
|
+
const config = resolveConfig2();
|
|
1042
|
+
const slug = orgSlugOverride ?? config.orgSlug;
|
|
832
1043
|
if (slug) {
|
|
833
1044
|
const match = orgs.find((o) => o.slug === slug);
|
|
834
1045
|
if (match) return match.id;
|
|
@@ -923,10 +1134,19 @@ Examples:
|
|
|
923
1134
|
$ atoll issue view ATOLL-42
|
|
924
1135
|
$ atoll project list
|
|
925
1136
|
$ atoll milestone list --project <id>
|
|
926
|
-
$ atoll config show`).option("--org <slug>", "Override default org slug (env: ATOLL_ORG)").option("--team <id>", "Override default team slug/ID (env: ATOLL_TEAM)").hook("preAction", (thisCommand) => {
|
|
1137
|
+
$ atoll config show`).option("--profile <name>", "Use a saved auth profile (env: ATOLL_PROFILE)").option("--org <slug>", "Override default org slug (env: ATOLL_ORG)").option("--team <id>", "Override default team slug/ID (env: ATOLL_TEAM)").hook("preAction", (thisCommand) => {
|
|
927
1138
|
const opts = thisCommand.opts();
|
|
928
|
-
if (opts.
|
|
929
|
-
|
|
1139
|
+
if (opts.profile) {
|
|
1140
|
+
process.env.ATOLL_PROFILE = opts.profile;
|
|
1141
|
+
}
|
|
1142
|
+
if (opts.org) {
|
|
1143
|
+
process.env.ATOLL_ORG = opts.org;
|
|
1144
|
+
process.env.ATOLL_CLI_ORG = opts.org;
|
|
1145
|
+
}
|
|
1146
|
+
if (opts.team) {
|
|
1147
|
+
process.env.ATOLL_TEAM = opts.team;
|
|
1148
|
+
process.env.ATOLL_CLI_TEAM = opts.team;
|
|
1149
|
+
}
|
|
930
1150
|
});
|
|
931
1151
|
program.addCommand(authCommand);
|
|
932
1152
|
program.addCommand(issueCommand);
|
|
@@ -959,8 +1179,8 @@ _atoll_completions() {
|
|
|
959
1179
|
local issue_cmds="list view create update delete assign unassign"
|
|
960
1180
|
local project_cmds="list create view"
|
|
961
1181
|
local milestone_cmds="list create"
|
|
962
|
-
local config_cmds="show set-org set-team"
|
|
963
|
-
local auth_cmds="login logout status"
|
|
1182
|
+
local config_cmds="show set-org set-team set-base-url"
|
|
1183
|
+
local auth_cmds="login logout status profiles use"
|
|
964
1184
|
local webhook_cmds="list create delete"
|
|
965
1185
|
local completion_cmds="bash zsh"
|
|
966
1186
|
|
|
@@ -1062,13 +1282,16 @@ _atoll() {
|
|
|
1062
1282
|
_values 'subcommand' \\
|
|
1063
1283
|
'show[Show configuration]' \\
|
|
1064
1284
|
'set-org[Set default org slug]' \\
|
|
1065
|
-
'set-team[Set default team]'
|
|
1285
|
+
'set-team[Set default team]' \\
|
|
1286
|
+
'set-base-url[Set default API base URL]'
|
|
1066
1287
|
;;
|
|
1067
1288
|
auth)
|
|
1068
1289
|
_values 'subcommand' \\
|
|
1069
1290
|
'login[Log in]' \\
|
|
1070
1291
|
'logout[Log out]' \\
|
|
1071
|
-
'status[Show auth status]'
|
|
1292
|
+
'status[Show auth status]' \\
|
|
1293
|
+
'profiles[List auth profiles]' \\
|
|
1294
|
+
'use[Set active auth profile]'
|
|
1072
1295
|
;;
|
|
1073
1296
|
webhook)
|
|
1074
1297
|
_values 'subcommand' \\
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/config.ts","../src/index.ts","../src/commands/auth.ts","../src/lib/client.ts","../src/lib/output.ts","../src/commands/issue.ts","../src/lib/colors.ts","../src/commands/comment.ts","../src/commands/project.ts","../src/commands/milestone.ts","../src/commands/config.ts","../src/commands/webhook.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport interface AtollConfig {\n apiKey?: string;\n orgSlug?: string;\n defaultTeam?: string;\n}\n\nconst CONFIG_DIR = join(homedir(), '.atoll');\nconst CONFIG_PATH = join(CONFIG_DIR, 'config.json');\n\nexport function readConfig(): AtollConfig {\n if (!existsSync(CONFIG_PATH)) return {};\n try {\n return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));\n } catch {\n return {};\n }\n}\n\nexport function writeConfig(config: AtollConfig): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nexport function deleteConfig(): void {\n if (existsSync(CONFIG_PATH)) {\n unlinkSync(CONFIG_PATH);\n }\n}\n\nexport function getApiKey(): string | undefined {\n return process.env.ATOLL_API_KEY || readConfig().apiKey;\n}\n","import { Command } from 'commander';\nimport { authCommand } from './commands/auth.js';\nimport { issueCommand } from './commands/issue.js';\nimport { commentCommand } from './commands/comment.js';\nimport { projectCommand } from './commands/project.js';\nimport { milestoneCommand } from './commands/milestone.js';\nimport { configCommand } from './commands/config.js';\nimport { webhookCommand } from './commands/webhook.js';\n\nconst program = new Command('atoll')\n .description('Atoll CLI — project management from the terminal')\n .version('0.1.0')\n .addHelpText('after', `\nExamples:\n $ atoll issue list\n $ atoll issue create --title \"Fix login bug\" --priority 1\n $ atoll issue view ATOLL-42\n $ atoll project list\n $ atoll milestone list --project <id>\n $ atoll config show`)\n // Global overrides — passed via env vars so sub-commands can pick them up\n .option('--org <slug>', 'Override default org slug (env: ATOLL_ORG)')\n .option('--team <id>', 'Override default team slug/ID (env: ATOLL_TEAM)')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts();\n if (opts.org) process.env.ATOLL_ORG = opts.org;\n if (opts.team) process.env.ATOLL_TEAM = opts.team;\n });\n\nprogram.addCommand(authCommand);\nprogram.addCommand(issueCommand);\nprogram.addCommand(commentCommand);\nprogram.addCommand(projectCommand);\nprogram.addCommand(milestoneCommand);\nprogram.addCommand(configCommand);\nprogram.addCommand(webhookCommand);\n\n// ── atoll completion ────────────────────────────────────────────────────────\n\nconst completionCommand = new Command('completion')\n .description('Output shell completion scripts')\n .addHelpText('after', `\nExamples:\n $ atoll completion bash >> ~/.bashrc\n $ atoll completion zsh >> ~/.zshrc`);\n\ncompletionCommand\n .command('bash')\n .description('Output bash completion script')\n .action(() => {\n process.stdout.write(bashCompletion());\n });\n\ncompletionCommand\n .command('zsh')\n .description('Output zsh completion script')\n .action(() => {\n process.stdout.write(zshCompletion());\n });\n\nprogram.addCommand(completionCommand);\n\nprogram.parse();\n\n// ---------------------------------------------------------------------------\n// Completion script generators\n// ---------------------------------------------------------------------------\n\nfunction bashCompletion(): string {\n return `# Atoll CLI bash completion\n# Add to ~/.bashrc: source <(atoll completion bash)\n\n_atoll_completions() {\n local cur prev words cword\n _init_completion || return\n\n local commands=\"auth issue comment project milestone config webhook completion\"\n local issue_cmds=\"list view create update delete assign unassign\"\n local project_cmds=\"list create view\"\n local milestone_cmds=\"list create\"\n local config_cmds=\"show set-org set-team\"\n local auth_cmds=\"login logout status\"\n local webhook_cmds=\"list create delete\"\n local completion_cmds=\"bash zsh\"\n\n if [ \"\\${COMP_CWORD}\" -eq 1 ]; then\n COMPREPLY=( $(compgen -W \"\\${commands}\" -- \"\\${cur}\") )\n return 0\n fi\n\n case \"\\${words[1]}\" in\n issue)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${issue_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n project)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${project_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n milestone)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${milestone_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n config)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${config_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n auth)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${auth_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n webhook)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${webhook_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n completion)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${completion_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n esac\n}\n\ncomplete -F _atoll_completions atoll\n`;\n}\n\nfunction zshCompletion(): string {\n return `#compdef atoll\n# Atoll CLI zsh completion\n# Add to ~/.zshrc: source <(atoll completion zsh)\n\n_atoll() {\n local state\n\n _arguments \\\\\n '1: :->command' \\\\\n '*: :->args'\n\n case \\$state in\n command)\n _values 'command' \\\\\n 'auth[Manage authentication]' \\\\\n 'issue[Manage issues]' \\\\\n 'comment[Manage comments]' \\\\\n 'project[Manage projects]' \\\\\n 'milestone[Manage milestones]' \\\\\n 'config[Manage CLI configuration]' \\\\\n 'webhook[Manage outbound webhooks]' \\\\\n 'completion[Output shell completion scripts]'\n ;;\n args)\n case \\$words[2] in\n issue)\n _values 'subcommand' \\\\\n 'list[List issues]' \\\\\n 'view[View issue details]' \\\\\n 'create[Create a new issue]' \\\\\n 'update[Update an issue]' \\\\\n 'delete[Delete an issue]' \\\\\n 'assign[Assign an issue]' \\\\\n 'unassign[Remove assignee]'\n ;;\n project)\n _values 'subcommand' \\\\\n 'list[List projects]' \\\\\n 'create[Create a project]' \\\\\n 'view[View project details]'\n ;;\n milestone)\n _values 'subcommand' \\\\\n 'list[List milestones]' \\\\\n 'create[Create a milestone]'\n ;;\n config)\n _values 'subcommand' \\\\\n 'show[Show configuration]' \\\\\n 'set-org[Set default org slug]' \\\\\n 'set-team[Set default team]'\n ;;\n auth)\n _values 'subcommand' \\\\\n 'login[Log in]' \\\\\n 'logout[Log out]' \\\\\n 'status[Show auth status]'\n ;;\n webhook)\n _values 'subcommand' \\\\\n 'list[List webhooks]' \\\\\n 'create[Create a webhook]' \\\\\n 'delete[Delete a webhook]'\n ;;\n completion)\n _values 'shell' 'bash' 'zsh'\n ;;\n esac\n ;;\n esac\n}\n\n_atoll \"\\$@\"\n`;\n}\n","import { Command } from 'commander';\nimport { readConfig, writeConfig, deleteConfig, getApiKey } from '../lib/config.js';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\n\nexport const authCommand = new Command('auth')\n .description('Manage authentication');\n\nauthCommand\n .command('login')\n .description('Save an API key to ~/.atoll/config.json')\n .requiredOption('--key <API_KEY>', 'API key to store')\n .action((opts: { key: string }) => {\n const config = readConfig();\n config.apiKey = opts.key;\n writeConfig(config);\n output(\n { status: 'ok', message: 'API key saved' },\n '✓ API key saved to ~/.atoll/config.json',\n );\n });\n\nauthCommand\n .command('status')\n .description('Show current auth status and user info')\n .action(async () => {\n const apiKey = getApiKey();\n if (!apiKey) {\n outputError('Not authenticated. Run `atoll auth login --key <API_KEY>` or set ATOLL_API_KEY.');\n process.exit(1);\n }\n\n try {\n const client = new AtollClient({ apiKey });\n const me = await client.get<{\n user?: { email?: string; name?: string };\n org?: { slug?: string; name?: string };\n }>('/api/auth/me');\n\n output(\n { status: 'authenticated', ...me },\n [\n '✓ Authenticated',\n me.user?.name ? ` User: ${me.user.name}` : null,\n me.user?.email ? ` Email: ${me.user.email}` : null,\n me.org?.name ? ` Org: ${me.org.name} (${me.org.slug})` : null,\n ]\n .filter(Boolean)\n .join('\\n'),\n );\n } catch (err) {\n const msg = (err as Error).message || '';\n if (/API (401|403):/.test(msg)) {\n outputError('API key is invalid or expired');\n process.exit(1);\n }\n outputError(`Auth check failed: ${msg}`);\n process.exit(1);\n }\n });\n\nauthCommand\n .command('logout')\n .description('Remove stored API key')\n .action(() => {\n // Only clear the API key — preserve org/team config settings\n const config = readConfig();\n delete config.apiKey;\n writeConfig(config);\n output(\n { status: 'ok', message: 'Logged out' },\n '✓ Logged out — API key removed (org/team config preserved)',\n );\n });\n","import { getApiKey } from './config.js';\nimport { outputError } from './output.js';\n\nconst DEFAULT_BASE_URL = 'https://atollhq.com';\n\nexport class AtollClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(opts?: { baseUrl?: string; apiKey?: string }) {\n this.baseUrl = opts?.baseUrl || process.env.ATOLL_BASE_URL || DEFAULT_BASE_URL;\n\n const key = opts?.apiKey || getApiKey();\n if (!key) {\n outputError('No API key found. Run `atoll auth login --key <API_KEY>` or set ATOLL_API_KEY.');\n process.exit(1);\n }\n this.apiKey = key;\n }\n\n async request<T = unknown>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const res = await fetch(url, {\n ...init,\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n ...init?.headers,\n },\n });\n\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`API ${res.status}: ${body}`);\n }\n\n return res.json() as Promise<T>;\n }\n\n async get<T = unknown>(path: string): Promise<T> {\n return this.request<T>(path, { method: 'GET' });\n }\n\n async post<T = unknown>(path: string, body?: unknown): Promise<T> {\n return this.request<T>(path, {\n method: 'POST',\n body: body ? JSON.stringify(body) : undefined,\n });\n }\n\n async patch<T = unknown>(path: string, body?: unknown): Promise<T> {\n return this.request<T>(path, {\n method: 'PATCH',\n body: body ? JSON.stringify(body) : undefined,\n });\n }\n\n async delete<T = unknown>(path: string): Promise<T> {\n return this.request<T>(path, { method: 'DELETE' });\n }\n}\n","export type OutputRecord = Record<string, unknown>;\n\nfunction isJsonMode(): boolean {\n if (process.env.OUTPUT_FORMAT === 'json') return true;\n return !process.stdout.isTTY;\n}\n\nexport function output(data: OutputRecord, humanReadable: string): void {\n if (isJsonMode()) {\n process.stdout.write(JSON.stringify(data) + '\\n');\n } else {\n console.log(humanReadable);\n }\n}\n\nexport function outputError(message: string): void {\n if (isJsonMode()) {\n process.stdout.write(JSON.stringify({ error: message }) + '\\n');\n } else {\n console.error(`Error: ${message}`);\n }\n}\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { bold, dim, gray, statusColor, priorityIcon, success } from '../lib/colors.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface Issue {\n id: string;\n title: string;\n description?: string | null;\n status: string;\n priority: number;\n number?: number | null;\n assignee_id?: string | null;\n team_id?: string | null;\n project_id?: string | null;\n due_date?: string | null;\n created_at?: string;\n updated_at?: string;\n sub_tasks?: unknown[];\n issue_labels?: { label_id: string; labels: { name: string; color: string } }[];\n}\n\ninterface ListResponse {\n issues: Issue[];\n total: number;\n limit: number;\n offset: number;\n}\n\ninterface SingleResponse {\n issue: Issue;\n}\n\ninterface Org {\n id: string;\n name: string;\n slug: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function handleApiError(err: unknown): never {\n const msg = (err as Error).message ?? String(err);\n if (/API 401\\b/.test(msg)) {\n outputError('Session expired — run `atoll auth login` to re-authenticate.');\n } else if (/API 403\\b/.test(msg)) {\n outputError('Permission denied. You do not have access to this resource.');\n } else {\n outputError(msg);\n }\n process.exit(1);\n}\n\nconst VALID_STATUSES = ['backlog', 'todo', 'in_progress', 'done', 'cancelled'] as const;\nconst PRIORITY_LABELS: Record<number, string> = { 0: 'Urgent', 1: 'High', 2: 'Medium', 3: 'Low' };\n\nexport async function resolveOrgId(client: AtollClient, orgSlugOverride?: string): Promise<string> {\n const { orgs } = await client.get<{ orgs: Org[] }>('/api/orgs');\n if (!orgs || orgs.length === 0) {\n outputError('No organizations found. Create one first.');\n process.exit(1);\n }\n if (orgs.length === 1) return orgs[0].id;\n\n // Resolve slug: CLI override → env var (set by --org global flag) → config file\n const { readConfig } = await import('../lib/config.js');\n const config = readConfig();\n const slug = orgSlugOverride ?? process.env.ATOLL_ORG ?? config.orgSlug;\n\n if (slug) {\n const match = orgs.find((o) => o.slug === slug);\n if (match) return match.id;\n outputError(`Org \"${slug}\" not found. Available: ${orgs.map((o) => o.slug).join(', ')}`);\n process.exit(1);\n }\n outputError(`Multiple orgs found. Use --org <slug> or run \\`atoll config set-org <slug>\\`. Available: ${orgs.map((o) => o.slug).join(', ')}`);\n process.exit(1);\n}\n\n/**\n * Resolve a human-readable identifier (e.g. \"ATOLL-42\" or \"42\") to a UUID.\n * Falls back to treating the identifier as a UUID if it doesn't match the pattern.\n */\nexport async function resolveIssueId(client: AtollClient, orgId: string, identifier: string): Promise<string> {\n // If it looks like a UUID already, return as-is\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identifier)) {\n return identifier;\n }\n\n // Extract number from PREFIX-NUMBER or plain number\n const match = identifier.match(/^(?:[A-Za-z]+-)?(\\d+)$/);\n if (!match) {\n outputError(`Invalid identifier: \"${identifier}\". Use a UUID or PREFIX-NUMBER (e.g. ATOLL-42).`);\n process.exit(2);\n }\n\n const num = parseInt(match[1], 10);\n\n // Use number filter to avoid pagination issues with large issue lists\n const { issues } = await client.get<ListResponse>(\n `/api/orgs/${orgId}/issues?number=${num}&limit=1`,\n );\n if (issues.length > 0 && issues[0].number === num) {\n return issues[0].id;\n }\n\n // Fallback: paginate through all issues if the API doesn't support ?number= filtering\n let offset = 0;\n const pageSize = 100;\n while (true) {\n const page = await client.get<ListResponse>(\n `/api/orgs/${orgId}/issues?limit=${pageSize}&offset=${offset}`,\n );\n const found = page.issues.find((i) => i.number === num);\n if (found) return found.id;\n offset += pageSize;\n if (offset >= page.total) break;\n }\n\n outputError(`Issue #${num} not found.`);\n process.exit(1);\n}\n\nfunction formatIdentifier(issue: Issue): string {\n return issue.number != null ? `ATOLL-${issue.number}` : issue.id.slice(0, 8);\n}\n\nfunction padEnd(str: string, len: number): string {\n return str.length >= len ? str.slice(0, len) : str + ' '.repeat(len - str.length);\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport const issueCommand = new Command('issue')\n .description('Manage issues')\n .addHelpText('after', `\nExamples:\n $ atoll issue list\n $ atoll issue list --status in_progress --priority 1\n $ atoll issue view ATOLL-42\n $ atoll issue create --title \"Fix login\" --priority 1 --status todo\n $ atoll issue update ATOLL-42 --status done\n $ atoll issue assign ATOLL-42 --to <user-id>`);\n\n// ---- list ----\nissueCommand\n .command('list')\n .description('List issues')\n .option('--status <status>', `Filter by status (${VALID_STATUSES.join(', ')})`)\n .option('--assignee <user>', 'Filter by assignee ID')\n .option('--priority <n>', 'Filter by priority (0=urgent, 1=high, 2=medium, 3=low)', parseInt)\n .option('--limit <n>', 'Max results (1-100)', parseInt)\n .action(async (opts: { status?: string; assignee?: string; priority?: number; limit?: number }) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n\n const params = new URLSearchParams();\n if (opts.status) params.set('status', opts.status);\n if (opts.assignee) params.set('assigneeId', opts.assignee);\n if (opts.priority !== undefined) params.set('priority', String(opts.priority));\n if (opts.limit !== undefined) params.set('limit', String(opts.limit));\n\n const qs = params.toString();\n const data = await client.get<ListResponse>(`/api/orgs/${orgId}/issues${qs ? `?${qs}` : ''}`);\n\n // JSON mode: NDJSON\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const issue of data.issues) {\n process.stdout.write(JSON.stringify(issue) + '\\n');\n }\n return;\n }\n\n // TTY: table\n if (data.issues.length === 0) {\n console.log('No issues found.');\n return;\n }\n\n const header = bold(`${padEnd('ID', 10)} ${padEnd('STATUS', 14)} ${padEnd('PRI', 8)} TITLE`);\n console.log(header);\n console.log('─'.repeat(82));\n for (const issue of data.issues) {\n const id = formatIdentifier(issue);\n const pri = PRIORITY_LABELS[issue.priority] ?? String(issue.priority);\n const title = issue.title.length > 50 ? issue.title.slice(0, 47) + '…' : issue.title;\n const icon = priorityIcon(issue.priority);\n console.log(`${padEnd(id, 10)} ${statusColor(issue.status, padEnd(issue.status, 14))} ${icon} ${padEnd(pri, 8)} ${title}`);\n }\n console.log(dim(`${data.issues.length} of ${data.total} issues`));\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- view ----\nissueCommand\n .command('view <identifier>')\n .description('View issue details (UUID or PREFIX-NUMBER e.g. ATOLL-42)')\n .action(async (identifier: string) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n const { issue } = await client.get<SingleResponse>(`/api/orgs/${orgId}/issues/${issueId}`);\n\n // JSON mode\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n process.stdout.write(JSON.stringify(issue) + '\\n');\n return;\n }\n\n // TTY: formatted detail\n const id = formatIdentifier(issue);\n const pri = PRIORITY_LABELS[issue.priority] ?? String(issue.priority);\n console.log(`${bold(id)} ${issue.title}`);\n console.log(`Status: ${statusColor(issue.status, issue.status)} Priority: ${priorityIcon(issue.priority)} ${pri}`);\n if (issue.assignee_id) console.log(`Assignee: ${issue.assignee_id}`);\n if (issue.due_date) console.log(`Due: ${issue.due_date}`);\n if (issue.description) {\n console.log(`\\n${issue.description}`);\n }\n if (issue.issue_labels && issue.issue_labels.length > 0) {\n const labels = issue.issue_labels.map((l) => l.labels.name).join(', ');\n console.log(`Labels: ${labels}`);\n }\n if (issue.sub_tasks && Array.isArray(issue.sub_tasks) && issue.sub_tasks.length > 0) {\n console.log(`\\nSubtasks: ${issue.sub_tasks.length}`);\n }\n if (issue.created_at) console.log(dim(`Created: ${issue.created_at}`));\n if (issue.updated_at) console.log(dim(`Updated: ${issue.updated_at}`));\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- create ----\nissueCommand\n .command('create')\n .description('Create a new issue')\n .requiredOption('--title <title>', 'Issue title')\n .option('--description <text>', 'Issue description')\n .option('--status <status>', `Status (${VALID_STATUSES.join(', ')})`)\n .option('--priority <n>', 'Priority (0=urgent, 1=high, 2=medium, 3=low)', parseInt)\n .action(async (opts: { title: string; description?: string; status?: string; priority?: number }) => {\n try {\n if (opts.status && !VALID_STATUSES.includes(opts.status as typeof VALID_STATUSES[number])) {\n outputError(`Invalid status \"${opts.status}\". Must be one of: ${VALID_STATUSES.join(', ')}`);\n process.exit(2);\n }\n if (opts.priority !== undefined && ![0, 1, 2, 3].includes(opts.priority)) {\n outputError('Priority must be 0 (urgent), 1 (high), 2 (medium), or 3 (low).');\n process.exit(2);\n }\n\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n\n const body: Record<string, unknown> = { title: opts.title };\n if (opts.description !== undefined) body.description = opts.description;\n if (opts.status) body.status = opts.status;\n if (opts.priority !== undefined) body.priority = opts.priority;\n\n const { issue } = await client.post<SingleResponse>(`/api/orgs/${orgId}/issues`, body);\n\n output(\n { issue },\n success(`Created ${formatIdentifier(issue)}: ${issue.title}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- update ----\nissueCommand\n .command('update <identifier>')\n .description('Update an issue')\n .option('--title <t>', 'New title')\n .option('--status <s>', `New status (${VALID_STATUSES.join(', ')})`)\n .option('--priority <p>', 'New priority (0-3)', parseInt)\n .action(async (identifier: string, opts: { title?: string; status?: string; priority?: number }) => {\n try {\n if (opts.status && !VALID_STATUSES.includes(opts.status as typeof VALID_STATUSES[number])) {\n outputError(`Invalid status \"${opts.status}\". Must be one of: ${VALID_STATUSES.join(', ')}`);\n process.exit(2);\n }\n if (opts.priority !== undefined && ![0, 1, 2, 3].includes(opts.priority)) {\n outputError('Priority must be 0-3.');\n process.exit(2);\n }\n\n const body: Record<string, unknown> = {};\n if (opts.title !== undefined) body.title = opts.title;\n if (opts.status) body.status = opts.status;\n if (opts.priority !== undefined) body.priority = opts.priority;\n\n if (Object.keys(body).length === 0) {\n outputError('No fields to update. Provide --title, --status, or --priority.');\n process.exit(2);\n }\n\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n const { issue } = await client.patch<SingleResponse>(`/api/orgs/${orgId}/issues/${issueId}`, body);\n\n output(\n { issue },\n success(`Updated ${formatIdentifier(issue)}: ${issue.title}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- delete ----\nissueCommand\n .command('delete <identifier>')\n .description('Delete an issue (admin/owner only)')\n .action(async (identifier: string) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n await client.delete(`/api/orgs/${orgId}/issues/${issueId}`);\n\n output(\n { success: true, id: issueId },\n success(`Deleted issue ${identifier}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- assign ----\nissueCommand\n .command('assign <identifier>')\n .description('Assign an issue to a user or agent')\n .requiredOption('--to <user>', 'User/agent ID or \"self\" for yourself')\n .action(async (identifier: string, opts: { to: string }) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n let assigneeId = opts.to;\n if (assigneeId === 'self') {\n const me = await client.get<{ auth?: { userId?: string } }>('/api/auth/me');\n // For agent keys, auth.userId IS the org_members.id directly\n const callerUserId = me.auth?.userId;\n if (!callerUserId) {\n outputError('Could not resolve your user ID.');\n process.exit(1);\n }\n // Agent callers: userId is the member id — use directly\n // Human callers: userId is the supabase auth id — look up member id\n const { members } = await client.get<{ members: { id: string; user_id: string }[] }>(\n `/api/orgs/${orgId}/members`,\n );\n const member = members.find((m) => m.id === callerUserId || m.user_id === callerUserId);\n if (!member) {\n outputError('You are not a member of this organisation.');\n process.exit(1);\n }\n assigneeId = member.id;\n }\n\n const { issue } = await client.patch<SingleResponse>(\n `/api/orgs/${orgId}/issues/${issueId}`,\n { assignee_id: assigneeId },\n );\n\n output(\n { issue },\n success(`Assigned ${formatIdentifier(issue)} to ${opts.to}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- unassign ----\nissueCommand\n .command('unassign <identifier>')\n .description('Remove assignee from an issue')\n .action(async (identifier: string) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n const { issue } = await client.patch<SingleResponse>(\n `/api/orgs/${orgId}/issues/${issueId}`,\n { assignee_id: null },\n );\n\n output(\n { issue },\n success(`Unassigned ${formatIdentifier(issue)}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n","/**\n * TTY-aware ANSI color helpers.\n * Colors are only emitted when stdout is a TTY and OUTPUT_FORMAT !== 'json'.\n */\n\nfunction isTTY(): boolean {\n return process.stdout.isTTY === true && process.env.OUTPUT_FORMAT !== 'json';\n}\n\nfunction ansi(code: string, text: string): string {\n if (!isTTY()) return text;\n return `\\x1b[${code}m${text}\\x1b[0m`;\n}\n\n// Basic colors\nexport const bold = (t: string) => ansi('1', t);\nexport const dim = (t: string) => ansi('2', t);\nexport const red = (t: string) => ansi('31', t);\nexport const green = (t: string) => ansi('32', t);\nexport const yellow = (t: string) => ansi('33', t);\nexport const blue = (t: string) => ansi('34', t);\nexport const cyan = (t: string) => ansi('36', t);\nexport const gray = (t: string) => ansi('90', t);\nexport const white = (t: string) => ansi('37', t);\n\n// Status badge colors\nexport function statusColor(status: string, text: string): string {\n switch (status) {\n case 'done': return green(text);\n case 'in_progress': return yellow(text);\n case 'backlog':\n case 'todo': return gray(text);\n case 'cancelled': return red(text);\n default: return text;\n }\n}\n\n// Priority icons\nexport function priorityIcon(priority: number): string {\n switch (priority) {\n case 0: return red('⚠⚠⚠'); // urgent\n case 1: return yellow('▄▆█'); // high\n case 2: return cyan('▄▆'); // medium\n case 3: return dim('---'); // low\n default: return String(priority);\n }\n}\n\n// Helpers\nexport const success = (msg: string) => green(`✓ ${msg}`);\nexport const error = (msg: string) => red(`Error: ${msg}`);\n\n// Raw ANSI codes (for use in format strings where needed)\nexport const RESET = isTTY() ? '\\x1b[0m' : '';\nexport const BOLD = isTTY() ? '\\x1b[1m' : '';\nexport const DIM = isTTY() ? '\\x1b[2m' : '';\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { handleApiError, resolveOrgId, resolveIssueId } from './issue.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface CommentAuthor {\n id: string;\n display_name: string;\n type: string;\n avatar_url?: string;\n}\n\ninterface Comment {\n id: string;\n body: string;\n author_id: string;\n author_type?: string;\n author?: CommentAuthor;\n created_at?: string;\n updated_at?: string;\n}\n\ninterface ListCommentsResponse {\n comments: Comment[];\n}\n\ninterface SingleCommentResponse {\n comment: Comment;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst BOLD = '\\x1b[1m';\nconst DIM = '\\x1b[2m';\nconst RESET = '\\x1b[0m';\nconst CYAN = '\\x1b[36m';\n\nfunction formatAuthor(comment: Comment): string {\n const name = comment.author?.display_name ?? comment.author_id;\n const badge = (comment.author?.type ?? comment.author_type) === 'agent' ? ' [agent]' : '';\n return `${name}${badge}`;\n}\n\nfunction formatTimestamp(ts?: string): string {\n if (!ts) return '';\n const d = new Date(ts);\n return d.toLocaleString();\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport const commentCommand = new Command('comment')\n .description('Manage issue comments');\n\n// ---- list ----\ncommentCommand\n .command('list <identifier>')\n .description('List comments on an issue (UUID or PREFIX-NUMBER e.g. ATOLL-42)')\n .action(async (identifier: string) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n const data = await client.get<ListCommentsResponse>(\n `/api/orgs/${orgId}/issues/${issueId}/comments`,\n );\n\n // JSON mode: NDJSON\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const comment of data.comments) {\n process.stdout.write(JSON.stringify(comment) + '\\n');\n }\n return;\n }\n\n // TTY: human-readable\n if (data.comments.length === 0) {\n console.log('No comments found.');\n return;\n }\n\n for (const comment of data.comments) {\n const author = formatAuthor(comment);\n const time = formatTimestamp(comment.created_at);\n console.log(`${BOLD}${CYAN}${author}${RESET} ${DIM}${time}${RESET}`);\n console.log(comment.body);\n console.log();\n }\n console.log(`${DIM}${data.comments.length} comment(s)${RESET}`);\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- add ----\ncommentCommand\n .command('add <identifier>')\n .description('Add a comment to an issue')\n .requiredOption('--body <text>', 'Comment body')\n .action(async (identifier: string, opts: { body: string }) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n const { comment } = await client.post<SingleCommentResponse>(\n `/api/orgs/${orgId}/issues/${issueId}/comments`,\n { body: opts.body },\n );\n\n output(\n { comment },\n `✓ Comment added to ${identifier}`,\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- update ----\ncommentCommand\n .command('update <comment-id>')\n .description('Update a comment')\n .requiredOption('--body <text>', 'New comment body')\n .requiredOption('--issue <identifier>', 'Issue identifier (e.g. UUID or PREFIX-NUMBER)')\n .action(async (commentId: string, opts: { body: string; issue: string }) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, opts.issue);\n\n const { comment } = await client.patch<SingleCommentResponse>(\n `/api/orgs/${orgId}/issues/${issueId}/comments/${commentId}`,\n { body: opts.body },\n );\n\n output(\n { comment },\n `✓ Comment ${commentId} updated`,\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- delete ----\ncommentCommand\n .command('delete <comment-id>')\n .description('Delete a comment')\n .requiredOption('--issue <identifier>', 'Issue identifier (e.g. UUID or PREFIX-NUMBER)')\n .action(async (commentId: string, opts: { issue: string }) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, opts.issue);\n\n await client.delete(`/api/orgs/${orgId}/issues/${issueId}/comments/${commentId}`);\n\n output(\n { success: true, id: commentId },\n `✓ Comment ${commentId} deleted`,\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { resolveOrgId, handleApiError } from './issue.js';\nimport { bold, dim, cyan, green, success } from '../lib/colors.js';\n\ninterface Project {\n id: string;\n name: string;\n description?: string | null;\n status: 'active' | 'archived';\n color: string;\n icon: string;\n issueCount?: number;\n completedCount?: number;\n progress?: number;\n created_at?: string;\n}\n\ninterface Issue {\n id: string;\n title: string;\n status: string;\n priority: number;\n number?: number | null;\n}\n\nfunction progressBar(progress: number, width = 20): string {\n const filled = Math.round((progress / 100) * width);\n const empty = width - filled;\n return `[${'█'.repeat(filled)}${'░'.repeat(empty)}] ${progress}%`;\n}\n\nexport const projectCommand = new Command('project')\n .description('Manage projects')\n .addHelpText('after', `\nExamples:\n $ atoll project list\n $ atoll project create --name \"My Project\" --icon 🚀\n $ atoll project view <project-id>`);\n\n// ── atoll project list ──────────────────────────────────────────────────────\n\nprojectCommand\n .command('list')\n .description('List all projects with progress')\n .action(async () => {\n const client = new AtollClient();\n try {\n const orgId = await resolveOrgId(client);\n const data = await client.get<{ projects: Project[] }>(`/api/orgs/${orgId}/projects`);\n const projects = data.projects ?? [];\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const p of projects) {\n process.stdout.write(JSON.stringify(p) + '\\n');\n }\n return;\n }\n\n if (projects.length === 0) {\n console.log(dim('No projects found.'));\n return;\n }\n\n for (const p of projects) {\n const progress = p.progress ?? 0;\n const bar = progressBar(progress);\n console.log(`${bold(`${p.icon} ${p.name}`)} ${dim(`(${p.id})`)}`);\n if (p.description) console.log(` ${dim(p.description)}`);\n console.log(` ${cyan(bar)} ${p.issueCount ?? 0} issues, ${p.completedCount ?? 0} done`);\n if (p.status === 'archived') console.log(` ${dim('[archived]')}`);\n console.log('');\n }\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ── atoll project create ────────────────────────────────────────────────────\n\nprojectCommand\n .command('create')\n .description('Create a new project')\n .requiredOption('--name <name>', 'Project name')\n .option('--description <desc>', 'Project description')\n .option('--color <color>', 'Project color (hex)', '#6366f1')\n .option('--icon <icon>', 'Project icon (emoji)', '📁')\n .action(async (opts) => {\n const client = new AtollClient();\n try {\n const orgId = await resolveOrgId(client);\n const data = await client.post<{ project: Project }>(`/api/orgs/${orgId}/projects`, {\n name: opts.name,\n description: opts.description ?? null,\n color: opts.color,\n icon: opts.icon,\n });\n\n output(\n { project: data.project },\n success(`Created project ${data.project.icon} ${data.project.name} (${dim(data.project.id)})`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ── atoll project view ──────────────────────────────────────────────────────\n\nprojectCommand\n .command('view <projectId>')\n .description('View project details and issues')\n .action(async (projectId) => {\n const client = new AtollClient();\n try {\n const orgId = await resolveOrgId(client);\n const data = await client.get<{ project: Project & { issues: Issue[] } }>(\n `/api/orgs/${orgId}/projects/${projectId}`,\n );\n const p = data.project;\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n process.stdout.write(JSON.stringify(p) + '\\n');\n return;\n }\n\n const progress = p.progress ?? 0;\n console.log(bold(`${p.icon} ${p.name}`));\n if (p.description) console.log(` ${p.description}`);\n console.log(` ${cyan(progressBar(progress))}`);\n console.log(` ${(p.issueCount ?? 0)} issues · ${(p.completedCount ?? 0)} completed`);\n console.log('');\n\n const issues: Issue[] = (p as unknown as { issues?: Issue[] }).issues ?? [];\n if (issues.length === 0) {\n console.log(dim('No issues in this project.'));\n return;\n }\n\n console.log(bold('Issues'));\n for (const issue of issues) {\n const num = issue.number ? `#${issue.number}` : issue.id.slice(0, 8);\n console.log(` ${dim(num)} ${issue.title} ${dim(`[${issue.status}]`)}`);\n }\n } catch (err) {\n handleApiError(err);\n }\n });\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { resolveOrgId, handleApiError } from './issue.js';\nimport { bold, dim, cyan, red, success } from '../lib/colors.js';\n\ninterface Milestone {\n id: string;\n name: string;\n description?: string | null;\n due_date?: string | null;\n status: 'active' | 'closed';\n issueCount?: number;\n completedCount?: number;\n progress?: number;\n isOverdue?: boolean;\n}\n\nfunction progressBar(progress: number, width = 20): string {\n const filled = Math.round((progress / 100) * width);\n const empty = width - filled;\n return `[${'█'.repeat(filled)}${'░'.repeat(empty)}] ${progress}%`;\n}\n\nexport const milestoneCommand = new Command('milestone')\n .description('Manage milestones')\n .addHelpText('after', `\nExamples:\n $ atoll milestone list --project <project-id>\n $ atoll milestone create --project <project-id> --name \"v1.0\" --date 2026-06-01`);\n\n// ── atoll milestone list ────────────────────────────────────────────────────\n\nmilestoneCommand\n .command('list')\n .description('List milestones for a project')\n .requiredOption('--project <id>', 'Project ID')\n .action(async (opts) => {\n const client = new AtollClient();\n try {\n const orgId = await resolveOrgId(client);\n const data = await client.get<{ milestones: Milestone[] }>(\n `/api/orgs/${orgId}/projects/${opts.project}/milestones`,\n );\n const milestones = data.milestones ?? [];\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const m of milestones) {\n process.stdout.write(JSON.stringify(m) + '\\n');\n }\n return;\n }\n\n if (milestones.length === 0) {\n console.log(dim('No milestones found.'));\n return;\n }\n\n for (const m of milestones) {\n const progress = m.progress ?? 0;\n const bar = progressBar(progress);\n const overdue = m.isOverdue ? ` ${red('[OVERDUE]')}` : '';\n console.log(`${bold(m.name)} ${dim(`(${m.id})`)}${overdue}`);\n if (m.due_date) console.log(` Due: ${m.due_date}`);\n if (m.description) console.log(` ${dim(m.description)}`);\n console.log(` ${cyan(bar)} ${m.issueCount ?? 0} issues, ${m.completedCount ?? 0} done`);\n if (m.status === 'closed') console.log(` ${dim('[closed]')}`);\n console.log('');\n }\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ── atoll milestone create ──────────────────────────────────────────────────\n\nmilestoneCommand\n .command('create')\n .description('Create a new milestone')\n .requiredOption('--project <id>', 'Project ID')\n .requiredOption('--name <name>', 'Milestone name')\n .option('--date <YYYY-MM-DD>', 'Due date')\n .option('--description <desc>', 'Description')\n .action(async (opts) => {\n const client = new AtollClient();\n try {\n const orgId = await resolveOrgId(client);\n const data = await client.post<{ milestone: Milestone }>(\n `/api/orgs/${orgId}/projects/${opts.project}/milestones`,\n {\n name: opts.name,\n description: opts.description ?? null,\n dueDate: opts.date ?? null,\n },\n );\n\n output(\n { milestone: data.milestone },\n success(`Milestone created: ${bold(data.milestone.name)} ${dim(`(${data.milestone.id})`)}`),\n );\n if (data.milestone.due_date) {\n console.log(` Due: ${data.milestone.due_date}`);\n }\n } catch (err) {\n handleApiError(err);\n }\n });\n","import { Command } from 'commander';\nimport { readConfig, writeConfig } from '../lib/config.js';\nimport { output, outputError } from '../lib/output.js';\nimport { success, bold, dim, gray } from '../lib/colors.js';\nimport { getApiKey } from '../lib/config.js';\n\nexport const configCommand = new Command('config')\n .description('Manage CLI configuration (org, team, API key)')\n .addHelpText('after', `\nExamples:\n $ atoll config show\n $ atoll config set-org my-org\n $ atoll config set-team team-abc123`);\n\n// ── atoll config show ───────────────────────────────────────────────────────\n\nconfigCommand\n .command('show')\n .description('Display current configuration')\n .action(() => {\n const cfg = readConfig();\n const apiKeySet = !!(process.env.ATOLL_API_KEY || cfg.apiKey);\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n output(\n {\n orgSlug: cfg.orgSlug ?? null,\n defaultTeam: cfg.defaultTeam ?? null,\n apiKeySet,\n },\n '',\n );\n return;\n }\n\n console.log(`${bold('Atoll CLI Configuration')}`);\n console.log(` ${dim('Org slug:')} ${cfg.orgSlug ? bold(cfg.orgSlug) : gray('(not set)')}`);\n console.log(` ${dim('Default team:')} ${cfg.defaultTeam ? bold(cfg.defaultTeam) : gray('(not set)')}`);\n console.log(` ${dim('API key:')} ${apiKeySet ? bold('set') : gray('not set — run `atoll auth login`')}`);\n });\n\n// ── atoll config set-org ────────────────────────────────────────────────────\n\nconfigCommand\n .command('set-org <slug>')\n .description('Set the default organisation slug')\n .action((slug: string) => {\n const cfg = readConfig();\n cfg.orgSlug = slug;\n writeConfig(cfg);\n\n output(\n { orgSlug: slug },\n success(`Default org set to \"${slug}\"`),\n );\n });\n\n// ── atoll config set-team ───────────────────────────────────────────────────\n\nconfigCommand\n .command('set-team <team>')\n .description('Set the default team slug or ID')\n .action((team: string) => {\n const cfg = readConfig();\n cfg.defaultTeam = team;\n writeConfig(cfg);\n\n output(\n { defaultTeam: team },\n success(`Default team set to \"${team}\"`),\n );\n });\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { bold, dim, green, gray, success } from '../lib/colors.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface Webhook {\n id: string;\n url: string;\n events: string[];\n enabled: boolean;\n created_at: string;\n}\n\ninterface WebhookListResponse {\n webhooks: Webhook[];\n}\n\ninterface WebhookCreateResponse {\n webhook: Webhook;\n secret: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction handleApiError(err: unknown): never {\n const msg = (err as Error).message ?? String(err);\n if (/API 401\\b/.test(msg)) {\n outputError('Session expired — run `atoll auth login` to re-authenticate.');\n } else if (/API 403\\b/.test(msg)) {\n outputError('Permission denied. You do not have access to this resource.');\n } else {\n outputError(msg);\n }\n process.exit(1);\n}\n\nasync function resolveOrgId(client: AtollClient, orgSlugOverride?: string): Promise<string> {\n const { orgs } = await client.get<{ orgs: { id: string; slug: string }[] }>('/api/orgs');\n if (!orgs || orgs.length === 0) {\n outputError('No organizations found.');\n process.exit(1);\n }\n if (orgs.length === 1) return orgs[0].id;\n\n const { readConfig } = await import('../lib/config.js');\n const config = readConfig();\n const slug = orgSlugOverride ?? process.env.ATOLL_ORG ?? config.orgSlug;\n\n if (slug) {\n const match = orgs.find((o) => o.slug === slug);\n if (match) return match.id;\n outputError(`Org \"${slug}\" not found. Available: ${orgs.map((o) => o.slug).join(', ')}`);\n process.exit(1);\n }\n outputError(`Multiple orgs found. Use --org <slug>.`);\n process.exit(1);\n}\n\nfunction padEnd(str: string, len: number): string {\n return str.length >= len ? str.slice(0, len) : str + ' '.repeat(len - str.length);\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport const webhookCommand = new Command('webhook')\n .description('Manage outbound webhooks')\n .addHelpText('after', `\nExamples:\n $ atoll webhook list\n $ atoll webhook create --url https://example.com/hook --events issue.created,issue.updated\n $ atoll webhook delete <id>`);\n\n// ---- list ----\nwebhookCommand\n .command('list')\n .description('List webhooks for the current org')\n .action(async () => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n\n const data = await client.get<WebhookListResponse>(`/api/webhooks?orgId=${orgId}`);\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const wh of data.webhooks) {\n process.stdout.write(JSON.stringify(wh) + '\\n');\n }\n return;\n }\n\n if (data.webhooks.length === 0) {\n console.log('No webhooks configured.');\n return;\n }\n\n const header = bold(`${padEnd('ID', 38)} ${padEnd('URL', 40)} ${padEnd('EVENTS', 20)} ACTIVE`);\n console.log(header);\n console.log('─'.repeat(100));\n for (const wh of data.webhooks) {\n const evts = wh.events.length === 0 ? 'all' : wh.events.join(',');\n const active = wh.enabled ? green('yes') : gray('no');\n const url = wh.url.length > 38 ? wh.url.slice(0, 35) + '…' : wh.url;\n const evtStr = evts.length > 18 ? evts.slice(0, 15) + '…' : evts;\n console.log(`${padEnd(wh.id.slice(0, 36), 38)} ${padEnd(url, 40)} ${padEnd(evtStr, 20)} ${active}`);\n }\n console.log(dim(`${data.webhooks.length} webhook(s)`));\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- create ----\nwebhookCommand\n .command('create')\n .description('Create a new webhook')\n .requiredOption('--url <url>', 'Payload URL (HTTPS)')\n .option('--events <events>', 'Comma-separated event types (default: all)', '')\n .action(async (opts: { url: string; events: string }) => {\n try {\n if (!opts.url.startsWith('https://')) {\n outputError('URL must use HTTPS.');\n process.exit(2);\n }\n\n const events = opts.events\n ? opts.events.split(',').map((e) => e.trim()).filter(Boolean)\n : [];\n\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n\n const data = await client.post<WebhookCreateResponse>(\n `/api/webhooks?orgId=${orgId}`,\n { url: opts.url, events },\n );\n\n output(\n { webhook: data.webhook, secret: data.secret },\n [\n success(`Created webhook ${data.webhook.id}`),\n '',\n bold('Signing secret (save this — shown only once):'),\n ` ${data.secret}`,\n ].join('\\n'),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- delete ----\nwebhookCommand\n .command('delete <id>')\n .description('Delete a webhook')\n .action(async (id: string) => {\n try {\n const client = new AtollClient();\n await client.delete(`/api/webhooks/${id}`);\n\n output(\n { success: true, id },\n success(`Deleted webhook ${id}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n"],"mappings":"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","names":["import_commander","import_commander","readConfig","import_commander","BOLD","DIM","RESET","import_commander","import_commander","progressBar","import_commander","import_commander","handleApiError","resolveOrgId","readConfig","padEnd"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/config.ts","../src/index.ts","../src/commands/auth.ts","../src/lib/client.ts","../src/lib/output.ts","../src/commands/issue.ts","../src/lib/colors.ts","../src/lib/urls.ts","../src/commands/comment.ts","../src/commands/project.ts","../src/commands/milestone.ts","../src/commands/config.ts","../src/commands/webhook.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport interface AtollConfig {\n apiKey?: string;\n orgSlug?: string;\n defaultTeam?: string;\n baseUrl?: string;\n activeProfile?: string;\n profiles?: Record<string, AtollProfile>;\n}\n\nexport interface AtollProfile {\n apiKey?: string;\n orgSlug?: string;\n defaultTeam?: string;\n baseUrl?: string;\n}\n\nexport interface ResolvedConfig extends AtollProfile {\n profile?: string;\n}\n\nconst CONFIG_DIR = join(homedir(), '.atoll');\nconst CONFIG_PATH = join(CONFIG_DIR, 'config.json');\n\nexport function readConfig(): AtollConfig {\n if (!existsSync(CONFIG_PATH)) return {};\n try {\n return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));\n } catch {\n return {};\n }\n}\n\nexport function writeConfig(config: AtollConfig): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nexport function deleteConfig(): void {\n if (existsSync(CONFIG_PATH)) {\n unlinkSync(CONFIG_PATH);\n }\n}\n\nexport function getActiveProfile(config = readConfig()): string | undefined {\n return process.env.ATOLL_PROFILE || config.activeProfile;\n}\n\nexport function getProfile(config: AtollConfig, profileName?: string): AtollProfile | undefined {\n if (!profileName) return undefined;\n return config.profiles?.[profileName];\n}\n\nexport function ensureProfile(config: AtollConfig, profileName: string): AtollProfile {\n config.profiles ??= {};\n config.profiles[profileName] ??= {};\n return config.profiles[profileName];\n}\n\nexport function resolveConfig(opts?: { profile?: string }): ResolvedConfig {\n const config = readConfig();\n const profileName = opts?.profile || getActiveProfile(config);\n const profile = getProfile(config, profileName);\n\n return {\n profile: profileName,\n apiKey: process.env.ATOLL_API_KEY || profile?.apiKey || (profileName ? undefined : config.apiKey),\n orgSlug: process.env.ATOLL_ORG || profile?.orgSlug || (profileName ? undefined : config.orgSlug),\n defaultTeam: process.env.ATOLL_TEAM || profile?.defaultTeam || (profileName ? undefined : config.defaultTeam),\n baseUrl: process.env.ATOLL_BASE_URL || profile?.baseUrl || (profileName ? undefined : config.baseUrl),\n };\n}\n\nexport function getApiKey(opts?: { profile?: string }): string | undefined {\n return resolveConfig(opts).apiKey;\n}\n","import { Command } from 'commander';\nimport { authCommand } from './commands/auth.js';\nimport { issueCommand } from './commands/issue.js';\nimport { commentCommand } from './commands/comment.js';\nimport { projectCommand } from './commands/project.js';\nimport { milestoneCommand } from './commands/milestone.js';\nimport { configCommand } from './commands/config.js';\nimport { webhookCommand } from './commands/webhook.js';\n\nconst program = new Command('atoll')\n .description('Atoll CLI — project management from the terminal')\n .version('0.1.0')\n .addHelpText('after', `\nExamples:\n $ atoll issue list\n $ atoll issue create --title \"Fix login bug\" --priority 1\n $ atoll issue view ATOLL-42\n $ atoll project list\n $ atoll milestone list --project <id>\n $ atoll config show`)\n // Global overrides — passed via env vars so sub-commands can pick them up\n .option('--profile <name>', 'Use a saved auth profile (env: ATOLL_PROFILE)')\n .option('--org <slug>', 'Override default org slug (env: ATOLL_ORG)')\n .option('--team <id>', 'Override default team slug/ID (env: ATOLL_TEAM)')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts();\n if (opts.profile) {\n process.env.ATOLL_PROFILE = opts.profile;\n }\n if (opts.org) {\n process.env.ATOLL_ORG = opts.org;\n process.env.ATOLL_CLI_ORG = opts.org;\n }\n if (opts.team) {\n process.env.ATOLL_TEAM = opts.team;\n process.env.ATOLL_CLI_TEAM = opts.team;\n }\n });\n\nprogram.addCommand(authCommand);\nprogram.addCommand(issueCommand);\nprogram.addCommand(commentCommand);\nprogram.addCommand(projectCommand);\nprogram.addCommand(milestoneCommand);\nprogram.addCommand(configCommand);\nprogram.addCommand(webhookCommand);\n\n// ── atoll completion ────────────────────────────────────────────────────────\n\nconst completionCommand = new Command('completion')\n .description('Output shell completion scripts')\n .addHelpText('after', `\nExamples:\n $ atoll completion bash >> ~/.bashrc\n $ atoll completion zsh >> ~/.zshrc`);\n\ncompletionCommand\n .command('bash')\n .description('Output bash completion script')\n .action(() => {\n process.stdout.write(bashCompletion());\n });\n\ncompletionCommand\n .command('zsh')\n .description('Output zsh completion script')\n .action(() => {\n process.stdout.write(zshCompletion());\n });\n\nprogram.addCommand(completionCommand);\n\nprogram.parse();\n\n// ---------------------------------------------------------------------------\n// Completion script generators\n// ---------------------------------------------------------------------------\n\nfunction bashCompletion(): string {\n return `# Atoll CLI bash completion\n# Add to ~/.bashrc: source <(atoll completion bash)\n\n_atoll_completions() {\n local cur prev words cword\n _init_completion || return\n\n local commands=\"auth issue comment project milestone config webhook completion\"\n local issue_cmds=\"list view create update delete assign unassign\"\n local project_cmds=\"list create view\"\n local milestone_cmds=\"list create\"\n local config_cmds=\"show set-org set-team set-base-url\"\n local auth_cmds=\"login logout status profiles use\"\n local webhook_cmds=\"list create delete\"\n local completion_cmds=\"bash zsh\"\n\n if [ \"\\${COMP_CWORD}\" -eq 1 ]; then\n COMPREPLY=( $(compgen -W \"\\${commands}\" -- \"\\${cur}\") )\n return 0\n fi\n\n case \"\\${words[1]}\" in\n issue)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${issue_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n project)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${project_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n milestone)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${milestone_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n config)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${config_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n auth)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${auth_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n webhook)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${webhook_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n completion)\n if [ \"\\${COMP_CWORD}\" -eq 2 ]; then\n COMPREPLY=( $(compgen -W \"\\${completion_cmds}\" -- \"\\${cur}\") )\n fi\n ;;\n esac\n}\n\ncomplete -F _atoll_completions atoll\n`;\n}\n\nfunction zshCompletion(): string {\n return `#compdef atoll\n# Atoll CLI zsh completion\n# Add to ~/.zshrc: source <(atoll completion zsh)\n\n_atoll() {\n local state\n\n _arguments \\\\\n '1: :->command' \\\\\n '*: :->args'\n\n case \\$state in\n command)\n _values 'command' \\\\\n 'auth[Manage authentication]' \\\\\n 'issue[Manage issues]' \\\\\n 'comment[Manage comments]' \\\\\n 'project[Manage projects]' \\\\\n 'milestone[Manage milestones]' \\\\\n 'config[Manage CLI configuration]' \\\\\n 'webhook[Manage outbound webhooks]' \\\\\n 'completion[Output shell completion scripts]'\n ;;\n args)\n case \\$words[2] in\n issue)\n _values 'subcommand' \\\\\n 'list[List issues]' \\\\\n 'view[View issue details]' \\\\\n 'create[Create a new issue]' \\\\\n 'update[Update an issue]' \\\\\n 'delete[Delete an issue]' \\\\\n 'assign[Assign an issue]' \\\\\n 'unassign[Remove assignee]'\n ;;\n project)\n _values 'subcommand' \\\\\n 'list[List projects]' \\\\\n 'create[Create a project]' \\\\\n 'view[View project details]'\n ;;\n milestone)\n _values 'subcommand' \\\\\n 'list[List milestones]' \\\\\n 'create[Create a milestone]'\n ;;\n config)\n _values 'subcommand' \\\\\n 'show[Show configuration]' \\\\\n 'set-org[Set default org slug]' \\\\\n 'set-team[Set default team]' \\\\\n 'set-base-url[Set default API base URL]'\n ;;\n auth)\n _values 'subcommand' \\\\\n 'login[Log in]' \\\\\n 'logout[Log out]' \\\\\n 'status[Show auth status]' \\\\\n 'profiles[List auth profiles]' \\\\\n 'use[Set active auth profile]'\n ;;\n webhook)\n _values 'subcommand' \\\\\n 'list[List webhooks]' \\\\\n 'create[Create a webhook]' \\\\\n 'delete[Delete a webhook]'\n ;;\n completion)\n _values 'shell' 'bash' 'zsh'\n ;;\n esac\n ;;\n esac\n}\n\n_atoll \"\\$@\"\n`;\n}\n","import { Command } from 'commander';\nimport {\n ensureProfile,\n getProfile,\n readConfig,\n resolveConfig,\n writeConfig,\n} from '../lib/config.js';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\n\nexport const authCommand = new Command('auth')\n .description('Manage authentication');\n\nauthCommand\n .command('login')\n .description('Save an API key to ~/.atoll/config.json')\n .requiredOption('--key <API_KEY>', 'API key to store')\n .option('--profile <name>', 'Profile name to store this API key under')\n .option('--org <slug>', 'Default org slug for this profile')\n .option('--team <team>', 'Default team slug or ID for this profile')\n .option('--base-url <url>', 'Base URL for this profile')\n .action((opts: { key: string; profile?: string; org?: string; team?: string; baseUrl?: string }) => {\n const config = readConfig();\n const profileName = opts.profile || process.env.ATOLL_PROFILE || config.activeProfile;\n const orgSlug = opts.org ?? process.env.ATOLL_CLI_ORG;\n const defaultTeam = opts.team ?? process.env.ATOLL_CLI_TEAM;\n const baseUrl = opts.baseUrl;\n\n if (profileName) {\n const profile = ensureProfile(config, profileName);\n profile.apiKey = opts.key;\n if (orgSlug !== undefined) profile.orgSlug = orgSlug;\n if (defaultTeam !== undefined) profile.defaultTeam = defaultTeam;\n if (baseUrl !== undefined) profile.baseUrl = baseUrl;\n config.activeProfile = profileName;\n } else {\n config.apiKey = opts.key;\n if (orgSlug !== undefined) config.orgSlug = orgSlug;\n if (defaultTeam !== undefined) config.defaultTeam = defaultTeam;\n if (baseUrl !== undefined) config.baseUrl = baseUrl;\n }\n\n writeConfig(config);\n output(\n { status: 'ok', message: 'API key saved', profile: profileName ?? null },\n profileName\n ? `✓ API key saved to profile \"${profileName}\" in ~/.atoll/config.json`\n : '✓ API key saved to ~/.atoll/config.json',\n );\n });\n\nauthCommand\n .command('status')\n .description('Show current auth status and user info')\n .option('--profile <name>', 'Profile name to check')\n .action(async (opts: { profile?: string }) => {\n const resolved = resolveConfig({ profile: opts.profile });\n const apiKey = resolved.apiKey;\n if (!apiKey) {\n const suffix = resolved.profile ? ` for profile \"${resolved.profile}\"` : '';\n outputError(`Not authenticated${suffix}. Run \\`atoll auth login --key <API_KEY>\\` or set ATOLL_API_KEY.`);\n process.exit(1);\n }\n\n try {\n const client = new AtollClient({ apiKey, baseUrl: resolved.baseUrl, profile: opts.profile });\n const me = await client.get<{\n user?: { email?: string; name?: string };\n org?: { slug?: string; name?: string };\n }>('/api/auth/me');\n\n output(\n { status: 'authenticated', ...me },\n [\n '✓ Authenticated',\n resolved.profile ? ` Profile: ${resolved.profile}` : null,\n me.user?.name ? ` User: ${me.user.name}` : null,\n me.user?.email ? ` Email: ${me.user.email}` : null,\n me.org?.name ? ` Org: ${me.org.name} (${me.org.slug})` : null,\n ]\n .filter(Boolean)\n .join('\\n'),\n );\n } catch (err) {\n const msg = (err as Error).message || '';\n if (/API (401|403):/.test(msg)) {\n outputError('API key is invalid or expired');\n process.exit(1);\n }\n outputError(`Auth check failed: ${msg}`);\n process.exit(1);\n }\n });\n\nauthCommand\n .command('profiles')\n .description('List saved auth profiles')\n .action(() => {\n const config = readConfig();\n const activeProfile = config.activeProfile;\n const profiles = Object.entries(config.profiles ?? {})\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, profile]) => ({\n name,\n active: name === activeProfile,\n apiKeySet: !!profile.apiKey,\n orgSlug: profile.orgSlug ?? null,\n defaultTeam: profile.defaultTeam ?? null,\n baseUrl: profile.baseUrl ?? null,\n }));\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n output({ activeProfile: activeProfile ?? null, profiles }, '');\n return;\n }\n\n if (profiles.length === 0) {\n console.log('No profiles configured.');\n return;\n }\n\n for (const profile of profiles) {\n const marker = profile.active ? '*' : ' ';\n const parts = [\n `${marker} ${profile.name}`,\n profile.apiKeySet ? 'key=set' : 'key=not set',\n profile.orgSlug ? `org=${profile.orgSlug}` : null,\n profile.defaultTeam ? `team=${profile.defaultTeam}` : null,\n profile.baseUrl ? `baseUrl=${profile.baseUrl}` : null,\n ].filter(Boolean);\n console.log(parts.join(' '));\n }\n });\n\nauthCommand\n .command('use <profile>')\n .description('Set the active auth profile')\n .action((profileName: string) => {\n const config = readConfig();\n if (!getProfile(config, profileName)) {\n outputError(`Profile \"${profileName}\" not found. Run \\`atoll auth login --profile ${profileName} --key <API_KEY>\\` first.`);\n process.exit(1);\n }\n\n config.activeProfile = profileName;\n writeConfig(config);\n output(\n { activeProfile: profileName },\n `✓ Active profile set to \"${profileName}\"`,\n );\n });\n\nauthCommand\n .command('logout')\n .description('Remove stored API key')\n .option('--profile <name>', 'Profile name to log out')\n .action((opts: { profile?: string }) => {\n // Only clear the API key — preserve org/team config settings.\n const config = readConfig();\n const profileName = opts.profile || process.env.ATOLL_PROFILE || config.activeProfile;\n\n if (profileName) {\n const profile = getProfile(config, profileName);\n if (!profile) {\n outputError(`Profile \"${profileName}\" not found.`);\n process.exit(1);\n }\n delete profile.apiKey;\n } else {\n delete config.apiKey;\n }\n\n writeConfig(config);\n output(\n { status: 'ok', message: 'Logged out', profile: profileName ?? null },\n profileName\n ? `✓ Logged out of profile \"${profileName}\" — API key removed (org/team config preserved)`\n : '✓ Logged out — API key removed (org/team config preserved)',\n );\n });\n","import { resolveConfig } from './config.js';\nimport { outputError } from './output.js';\n\nconst DEFAULT_BASE_URL = 'https://atollhq.com';\n\nexport class AtollClient {\n readonly baseUrl: string;\n private apiKey: string;\n\n constructor(opts?: { baseUrl?: string; apiKey?: string; profile?: string }) {\n const config = resolveConfig({ profile: opts?.profile });\n this.baseUrl = opts?.baseUrl || config.baseUrl || DEFAULT_BASE_URL;\n\n const key = opts?.apiKey || config.apiKey;\n if (!key) {\n const suffix = config.profile ? ` for profile \"${config.profile}\"` : '';\n outputError(`No API key found${suffix}. Run \\`atoll auth login --key <API_KEY>\\` or set ATOLL_API_KEY.`);\n process.exit(1);\n }\n this.apiKey = key;\n }\n\n async request<T = unknown>(path: string, init?: RequestInit): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const res = await fetch(url, {\n ...init,\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n ...init?.headers,\n },\n });\n\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`API ${res.status}: ${body}`);\n }\n\n return res.json() as Promise<T>;\n }\n\n async get<T = unknown>(path: string): Promise<T> {\n return this.request<T>(path, { method: 'GET' });\n }\n\n async post<T = unknown>(path: string, body?: unknown): Promise<T> {\n return this.request<T>(path, {\n method: 'POST',\n body: body ? JSON.stringify(body) : undefined,\n });\n }\n\n async patch<T = unknown>(path: string, body?: unknown): Promise<T> {\n return this.request<T>(path, {\n method: 'PATCH',\n body: body ? JSON.stringify(body) : undefined,\n });\n }\n\n async delete<T = unknown>(path: string): Promise<T> {\n return this.request<T>(path, { method: 'DELETE' });\n }\n}\n","export type OutputRecord = Record<string, unknown>;\n\nfunction isJsonMode(): boolean {\n if (process.env.OUTPUT_FORMAT === 'json') return true;\n return !process.stdout.isTTY;\n}\n\nexport function output(data: OutputRecord, humanReadable: string): void {\n if (isJsonMode()) {\n process.stdout.write(JSON.stringify(data) + '\\n');\n } else {\n console.log(humanReadable);\n }\n}\n\nexport function outputError(message: string): void {\n if (isJsonMode()) {\n process.stdout.write(JSON.stringify({ error: message }) + '\\n');\n } else {\n console.error(`Error: ${message}`);\n }\n}\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { bold, dim, gray, statusColor, priorityIcon, success } from '../lib/colors.js';\nimport { buildIssueUrl, derivePrefix } from '../lib/urls.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface Issue {\n id: string;\n title: string;\n description?: string | null;\n status: string;\n priority: number;\n number?: number | null;\n assignee_id?: string | null;\n team_id?: string | null;\n project_id?: string | null;\n due_date?: string | null;\n created_at?: string;\n updated_at?: string;\n sub_tasks?: unknown[];\n issue_labels?: { label_id: string; labels: { name: string; color: string } }[];\n}\n\ninterface ListResponse {\n issues: Issue[];\n total: number;\n limit: number;\n offset: number;\n}\n\ninterface SingleResponse {\n issue: Issue;\n}\n\ninterface Org {\n id: string;\n name: string;\n slug: string;\n}\n\ninterface ProjectSummary {\n id: string;\n name: string;\n slug?: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function handleApiError(err: unknown): never {\n const msg = (err as Error).message ?? String(err);\n if (/API 401\\b/.test(msg)) {\n outputError('Session expired — run `atoll auth login` to re-authenticate.');\n } else if (/API 403\\b/.test(msg)) {\n outputError('Permission denied. You do not have access to this resource.');\n } else {\n outputError(msg);\n }\n process.exit(1);\n}\n\nconst VALID_STATUSES = ['backlog', 'todo', 'in_progress', 'done', 'cancelled'] as const;\nconst PRIORITY_LABELS: Record<number, string> = { 0: 'Urgent', 1: 'High', 2: 'Medium', 3: 'Low' };\n\nexport async function resolveOrg(client: AtollClient, orgSlugOverride?: string): Promise<Org> {\n const { orgs } = await client.get<{ orgs: Org[] }>('/api/orgs');\n if (!orgs || orgs.length === 0) {\n outputError('No organizations found. Create one first.');\n process.exit(1);\n }\n if (orgs.length === 1) return orgs[0];\n\n // Resolve slug: explicit override -> selected profile/env -> legacy config.\n const { resolveConfig } = await import('../lib/config.js');\n const config = resolveConfig();\n const slug = orgSlugOverride ?? config.orgSlug;\n\n if (slug) {\n const match = orgs.find((o) => o.slug === slug);\n if (match) return match;\n outputError(`Org \"${slug}\" not found. Available: ${orgs.map((o) => o.slug).join(', ')}`);\n process.exit(1);\n }\n outputError(`Multiple orgs found. Use --org <slug> or run \\`atoll config set-org <slug>\\`. Available: ${orgs.map((o) => o.slug).join(', ')}`);\n process.exit(1);\n}\n\nexport async function resolveOrgId(client: AtollClient, orgSlugOverride?: string): Promise<string> {\n return (await resolveOrg(client, orgSlugOverride)).id;\n}\n\n/**\n * Fetch the org's projects and return a map keyed by project id.\n * Used to derive PREFIX-NUMBER identifiers and canonical URLs for issues.\n */\nexport async function fetchProjectMap(\n client: AtollClient,\n orgId: string,\n): Promise<Map<string, ProjectSummary>> {\n const { projects } = await client.get<{ projects: ProjectSummary[] }>(\n `/api/orgs/${orgId}/projects`,\n );\n const map = new Map<string, ProjectSummary>();\n for (const p of projects ?? []) map.set(p.id, p);\n return map;\n}\n\nexport interface IssueWithUrl extends Issue {\n url: string | null;\n}\n\nexport function attachIssueUrl(\n issue: Issue,\n baseUrl: string,\n orgSlug: string,\n projects: Map<string, ProjectSummary>,\n): IssueWithUrl {\n const project = issue.project_id ? projects.get(issue.project_id) : undefined;\n return {\n ...issue,\n url: buildIssueUrl(baseUrl, orgSlug, project?.slug, issue.number ?? null),\n };\n}\n\n/**\n * Resolve a human-readable identifier (e.g. \"ATOLL-42\" or \"42\") to a UUID.\n * Falls back to treating the identifier as a UUID if it doesn't match the pattern.\n */\nexport async function resolveIssueId(client: AtollClient, orgId: string, identifier: string): Promise<string> {\n // If it looks like a UUID already, return as-is\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identifier)) {\n return identifier;\n }\n\n // Extract number from PREFIX-NUMBER or plain number. Prefix may contain\n // digits (e.g. \"Q2-42\") since derivePrefix preserves alphanumerics.\n const match = identifier.match(/^(?:[A-Za-z0-9]+-)?(\\d+)$/);\n if (!match) {\n outputError(`Invalid identifier: \"${identifier}\". Use a UUID or PREFIX-NUMBER (e.g. ATOLL-42).`);\n process.exit(2);\n }\n\n const num = parseInt(match[1], 10);\n\n // Use number filter to avoid pagination issues with large issue lists\n const { issues } = await client.get<ListResponse>(\n `/api/orgs/${orgId}/issues?number=${num}&limit=1`,\n );\n if (issues.length > 0 && issues[0].number === num) {\n return issues[0].id;\n }\n\n // Fallback: paginate through all issues if the API doesn't support ?number= filtering\n let offset = 0;\n const pageSize = 100;\n while (true) {\n const page = await client.get<ListResponse>(\n `/api/orgs/${orgId}/issues?limit=${pageSize}&offset=${offset}`,\n );\n const found = page.issues.find((i) => i.number === num);\n if (found) return found.id;\n offset += pageSize;\n if (offset >= page.total) break;\n }\n\n outputError(`Issue #${num} not found.`);\n process.exit(1);\n}\n\nfunction formatIdentifier(issue: Issue, projects?: Map<string, ProjectSummary>): string {\n if (issue.number == null) return issue.id.slice(0, 8);\n if (projects && issue.project_id) {\n const project = projects.get(issue.project_id);\n if (project?.name) return `${derivePrefix(project.name)}-${issue.number}`;\n }\n return `ATOLL-${issue.number}`;\n}\n\nfunction padEnd(str: string, len: number): string {\n return str.length >= len ? str.slice(0, len) : str + ' '.repeat(len - str.length);\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport const issueCommand = new Command('issue')\n .description('Manage issues')\n .addHelpText('after', `\nExamples:\n $ atoll issue list\n $ atoll issue list --status in_progress --priority 1\n $ atoll issue view ATOLL-42\n $ atoll issue create --title \"Fix login\" --priority 1 --status todo\n $ atoll issue update ATOLL-42 --status done\n $ atoll issue assign ATOLL-42 --to <user-id>`);\n\n// ---- list ----\nissueCommand\n .command('list')\n .description('List issues')\n .option('--status <status>', `Filter by status (${VALID_STATUSES.join(', ')})`)\n .option('--assignee <user>', 'Filter by assignee ID')\n .option('--priority <n>', 'Filter by priority (0=urgent, 1=high, 2=medium, 3=low)', parseInt)\n .option('--limit <n>', 'Max results (1-100)', parseInt)\n .action(async (opts: { status?: string; assignee?: string; priority?: number; limit?: number }) => {\n try {\n const client = new AtollClient();\n const org = await resolveOrg(client);\n\n const params = new URLSearchParams();\n if (opts.status) params.set('status', opts.status);\n if (opts.assignee) params.set('assigneeId', opts.assignee);\n if (opts.priority !== undefined) params.set('priority', String(opts.priority));\n if (opts.limit !== undefined) params.set('limit', String(opts.limit));\n\n const qs = params.toString();\n const [data, projects] = await Promise.all([\n client.get<ListResponse>(`/api/orgs/${org.id}/issues${qs ? `?${qs}` : ''}`),\n fetchProjectMap(client, org.id),\n ]);\n\n const enriched = data.issues.map((issue) =>\n attachIssueUrl(issue, client.baseUrl, org.slug, projects),\n );\n\n // JSON mode: NDJSON\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const issue of enriched) {\n process.stdout.write(JSON.stringify(issue) + '\\n');\n }\n return;\n }\n\n // TTY: table\n if (enriched.length === 0) {\n console.log('No issues found.');\n return;\n }\n\n const header = bold(`${padEnd('ID', 10)} ${padEnd('STATUS', 14)} ${padEnd('PRI', 8)} TITLE`);\n console.log(header);\n console.log('─'.repeat(82));\n for (const issue of enriched) {\n const id = formatIdentifier(issue, projects);\n const pri = PRIORITY_LABELS[issue.priority] ?? String(issue.priority);\n const title = issue.title.length > 50 ? issue.title.slice(0, 47) + '…' : issue.title;\n const icon = priorityIcon(issue.priority);\n console.log(`${padEnd(id, 10)} ${statusColor(issue.status, padEnd(issue.status, 14))} ${icon} ${padEnd(pri, 8)} ${title}`);\n }\n console.log(dim(`${enriched.length} of ${data.total} issues`));\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- view ----\nissueCommand\n .command('view <identifier>')\n .description('View issue details (UUID or PREFIX-NUMBER e.g. ATOLL-42)')\n .action(async (identifier: string) => {\n try {\n const client = new AtollClient();\n const org = await resolveOrg(client);\n const issueId = await resolveIssueId(client, org.id, identifier);\n\n const [{ issue }, projects] = await Promise.all([\n client.get<SingleResponse>(`/api/orgs/${org.id}/issues/${issueId}`),\n fetchProjectMap(client, org.id),\n ]);\n\n const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);\n\n // JSON mode\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n process.stdout.write(JSON.stringify(enriched) + '\\n');\n return;\n }\n\n // TTY: formatted detail\n const id = formatIdentifier(enriched, projects);\n const pri = PRIORITY_LABELS[enriched.priority] ?? String(enriched.priority);\n console.log(`${bold(id)} ${enriched.title}`);\n console.log(`Status: ${statusColor(enriched.status, enriched.status)} Priority: ${priorityIcon(enriched.priority)} ${pri}`);\n if (enriched.url) console.log(`URL: ${enriched.url}`);\n if (enriched.assignee_id) console.log(`Assignee: ${enriched.assignee_id}`);\n if (enriched.due_date) console.log(`Due: ${enriched.due_date}`);\n if (enriched.description) {\n console.log(`\\n${enriched.description}`);\n }\n if (enriched.issue_labels && enriched.issue_labels.length > 0) {\n const labels = enriched.issue_labels.map((l) => l.labels.name).join(', ');\n console.log(`Labels: ${labels}`);\n }\n if (enriched.sub_tasks && Array.isArray(enriched.sub_tasks) && enriched.sub_tasks.length > 0) {\n console.log(`\\nSubtasks: ${enriched.sub_tasks.length}`);\n }\n if (enriched.created_at) console.log(dim(`Created: ${enriched.created_at}`));\n if (enriched.updated_at) console.log(dim(`Updated: ${enriched.updated_at}`));\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- create ----\nissueCommand\n .command('create')\n .description('Create a new issue')\n .requiredOption('--title <title>', 'Issue title')\n .option('--description <text>', 'Issue description')\n .option('--status <status>', `Status (${VALID_STATUSES.join(', ')})`)\n .option('--priority <n>', 'Priority (0=urgent, 1=high, 2=medium, 3=low)', parseInt)\n .action(async (opts: { title: string; description?: string; status?: string; priority?: number }) => {\n try {\n if (opts.status && !VALID_STATUSES.includes(opts.status as typeof VALID_STATUSES[number])) {\n outputError(`Invalid status \"${opts.status}\". Must be one of: ${VALID_STATUSES.join(', ')}`);\n process.exit(2);\n }\n if (opts.priority !== undefined && ![0, 1, 2, 3].includes(opts.priority)) {\n outputError('Priority must be 0 (urgent), 1 (high), 2 (medium), or 3 (low).');\n process.exit(2);\n }\n\n const client = new AtollClient();\n const org = await resolveOrg(client);\n\n const body: Record<string, unknown> = { title: opts.title };\n if (opts.description !== undefined) body.description = opts.description;\n if (opts.status) body.status = opts.status;\n if (opts.priority !== undefined) body.priority = opts.priority;\n\n const { issue } = await client.post<SingleResponse>(`/api/orgs/${org.id}/issues`, body);\n const projects = await fetchProjectMap(client, org.id);\n const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);\n\n output(\n { issue: enriched },\n success(`Created ${formatIdentifier(enriched, projects)}: ${enriched.title}${enriched.url ? `\\n${enriched.url}` : ''}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- update ----\nissueCommand\n .command('update <identifier>')\n .description('Update an issue')\n .option('--title <t>', 'New title')\n .option('--status <s>', `New status (${VALID_STATUSES.join(', ')})`)\n .option('--priority <p>', 'New priority (0-3)', parseInt)\n .action(async (identifier: string, opts: { title?: string; status?: string; priority?: number }) => {\n try {\n if (opts.status && !VALID_STATUSES.includes(opts.status as typeof VALID_STATUSES[number])) {\n outputError(`Invalid status \"${opts.status}\". Must be one of: ${VALID_STATUSES.join(', ')}`);\n process.exit(2);\n }\n if (opts.priority !== undefined && ![0, 1, 2, 3].includes(opts.priority)) {\n outputError('Priority must be 0-3.');\n process.exit(2);\n }\n\n const body: Record<string, unknown> = {};\n if (opts.title !== undefined) body.title = opts.title;\n if (opts.status) body.status = opts.status;\n if (opts.priority !== undefined) body.priority = opts.priority;\n\n if (Object.keys(body).length === 0) {\n outputError('No fields to update. Provide --title, --status, or --priority.');\n process.exit(2);\n }\n\n const client = new AtollClient();\n const org = await resolveOrg(client);\n const issueId = await resolveIssueId(client, org.id, identifier);\n\n const { issue } = await client.patch<SingleResponse>(`/api/orgs/${org.id}/issues/${issueId}`, body);\n const projects = await fetchProjectMap(client, org.id);\n const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);\n\n output(\n { issue: enriched },\n success(`Updated ${formatIdentifier(enriched, projects)}: ${enriched.title}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- delete ----\nissueCommand\n .command('delete <identifier>')\n .description('Delete an issue (admin/owner only)')\n .action(async (identifier: string) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n await client.delete(`/api/orgs/${orgId}/issues/${issueId}`);\n\n output(\n { success: true, id: issueId },\n success(`Deleted issue ${identifier}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- assign ----\nissueCommand\n .command('assign <identifier>')\n .description('Assign an issue to a user or agent')\n .requiredOption('--to <user>', 'User/agent ID or \"self\" for yourself')\n .action(async (identifier: string, opts: { to: string }) => {\n try {\n const client = new AtollClient();\n const org = await resolveOrg(client);\n const issueId = await resolveIssueId(client, org.id, identifier);\n\n let assigneeId = opts.to;\n if (assigneeId === 'self') {\n const me = await client.get<{ auth?: { userId?: string } }>('/api/auth/me');\n // For agent keys, auth.userId IS the org_members.id directly\n const callerUserId = me.auth?.userId;\n if (!callerUserId) {\n outputError('Could not resolve your user ID.');\n process.exit(1);\n }\n // Agent callers: userId is the member id — use directly\n // Human callers: userId is the supabase auth id — look up member id\n const { members } = await client.get<{ members: { id: string; user_id: string }[] }>(\n `/api/orgs/${org.id}/members`,\n );\n const member = members.find((m) => m.id === callerUserId || m.user_id === callerUserId);\n if (!member) {\n outputError('You are not a member of this organisation.');\n process.exit(1);\n }\n assigneeId = member.id;\n }\n\n const { issue } = await client.patch<SingleResponse>(\n `/api/orgs/${org.id}/issues/${issueId}`,\n { assignee_id: assigneeId },\n );\n const projects = await fetchProjectMap(client, org.id);\n const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);\n\n output(\n { issue: enriched },\n success(`Assigned ${formatIdentifier(enriched, projects)} to ${opts.to}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- unassign ----\nissueCommand\n .command('unassign <identifier>')\n .description('Remove assignee from an issue')\n .action(async (identifier: string) => {\n try {\n const client = new AtollClient();\n const org = await resolveOrg(client);\n const issueId = await resolveIssueId(client, org.id, identifier);\n\n const { issue } = await client.patch<SingleResponse>(\n `/api/orgs/${org.id}/issues/${issueId}`,\n { assignee_id: null },\n );\n const projects = await fetchProjectMap(client, org.id);\n const enriched = attachIssueUrl(issue, client.baseUrl, org.slug, projects);\n\n output(\n { issue: enriched },\n success(`Unassigned ${formatIdentifier(enriched, projects)}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n","/**\n * TTY-aware ANSI color helpers.\n * Colors are only emitted when stdout is a TTY and OUTPUT_FORMAT !== 'json'.\n */\n\nfunction isTTY(): boolean {\n return process.stdout.isTTY === true && process.env.OUTPUT_FORMAT !== 'json';\n}\n\nfunction ansi(code: string, text: string): string {\n if (!isTTY()) return text;\n return `\\x1b[${code}m${text}\\x1b[0m`;\n}\n\n// Basic colors\nexport const bold = (t: string) => ansi('1', t);\nexport const dim = (t: string) => ansi('2', t);\nexport const red = (t: string) => ansi('31', t);\nexport const green = (t: string) => ansi('32', t);\nexport const yellow = (t: string) => ansi('33', t);\nexport const blue = (t: string) => ansi('34', t);\nexport const cyan = (t: string) => ansi('36', t);\nexport const gray = (t: string) => ansi('90', t);\nexport const white = (t: string) => ansi('37', t);\n\n// Status badge colors\nexport function statusColor(status: string, text: string): string {\n switch (status) {\n case 'done': return green(text);\n case 'in_progress': return yellow(text);\n case 'backlog':\n case 'todo': return gray(text);\n case 'cancelled': return red(text);\n default: return text;\n }\n}\n\n// Priority icons\nexport function priorityIcon(priority: number): string {\n switch (priority) {\n case 0: return red('⚠⚠⚠'); // urgent\n case 1: return yellow('▄▆█'); // high\n case 2: return cyan('▄▆'); // medium\n case 3: return dim('---'); // low\n default: return String(priority);\n }\n}\n\n// Helpers\nexport const success = (msg: string) => green(`✓ ${msg}`);\nexport const error = (msg: string) => red(`Error: ${msg}`);\n\n// Raw ANSI codes (for use in format strings where needed)\nexport const RESET = isTTY() ? '\\x1b[0m' : '';\nexport const BOLD = isTTY() ? '\\x1b[1m' : '';\nexport const DIM = isTTY() ? '\\x1b[2m' : '';\n","// URL builders for Atoll canonical web routes.\n// Mirrors the routing and identifier conventions in agent-pm.\n\n/**\n * Derive a short uppercase prefix from a project name.\n * Mirrors agent-pm/lib/issue-identifier.ts — keep in sync.\n */\nexport function derivePrefix(name: string): string {\n const cleaned = name.replace(/[^a-zA-Z0-9\\s]/g, ' ').trim();\n const words = cleaned.split(/\\s+/).filter(Boolean);\n\n if (words.length === 0) return 'TSK';\n\n if (words.length >= 2) {\n return words\n .slice(0, 5)\n .map((w) => w[0])\n .join('')\n .toUpperCase();\n }\n\n const word = words[0];\n if (word.length <= 4) return word.toUpperCase();\n return word.slice(0, 3).toUpperCase();\n}\n\nexport function issueIdentifier(\n projectName: string | null | undefined,\n issueNumber: number | null | undefined,\n): string | null {\n if (issueNumber == null) return null;\n const prefix = projectName ? derivePrefix(projectName) : 'TSK';\n return `${prefix}-${issueNumber}`;\n}\n\nfunction stripTrailingSlash(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n}\n\nexport function buildIssueUrl(\n baseUrl: string,\n orgSlug: string,\n projectSlug: string | null | undefined,\n issueNumber: number | null | undefined,\n): string | null {\n // Use the project-scoped route (/{org}/projects/{projectSlug}/issues/{number})\n // rather than the prefix-based identifier route. The prefix route is\n // ambiguous (two projects can derive the same prefix, and numeric-containing\n // prefixes like \"Q2\" fail the web app's [A-Z]{2,} regex).\n if (!orgSlug || !projectSlug || issueNumber == null) return null;\n return `${stripTrailingSlash(baseUrl)}/${orgSlug}/projects/${projectSlug}/issues/${issueNumber}`;\n}\n\nexport function buildProjectUrl(\n baseUrl: string,\n orgSlug: string,\n projectSlug: string | null | undefined,\n): string | null {\n if (!orgSlug || !projectSlug) return null;\n return `${stripTrailingSlash(baseUrl)}/${orgSlug}/projects/${projectSlug}`;\n}\n\nexport function buildInitiativeUrl(\n baseUrl: string,\n orgSlug: string,\n initiativeId: string | null | undefined,\n): string | null {\n if (!orgSlug || !initiativeId) return null;\n return `${stripTrailingSlash(baseUrl)}/${orgSlug}/initiatives/${initiativeId}`;\n}\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { handleApiError, resolveOrgId, resolveIssueId } from './issue.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface CommentAuthor {\n id: string;\n display_name: string;\n type: string;\n avatar_url?: string;\n}\n\ninterface Comment {\n id: string;\n body: string;\n author_id: string;\n author_type?: string;\n author?: CommentAuthor;\n created_at?: string;\n updated_at?: string;\n}\n\ninterface ListCommentsResponse {\n comments: Comment[];\n}\n\ninterface SingleCommentResponse {\n comment: Comment;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst BOLD = '\\x1b[1m';\nconst DIM = '\\x1b[2m';\nconst RESET = '\\x1b[0m';\nconst CYAN = '\\x1b[36m';\n\nfunction formatAuthor(comment: Comment): string {\n const name = comment.author?.display_name ?? comment.author_id;\n const badge = (comment.author?.type ?? comment.author_type) === 'agent' ? ' [agent]' : '';\n return `${name}${badge}`;\n}\n\nfunction formatTimestamp(ts?: string): string {\n if (!ts) return '';\n const d = new Date(ts);\n return d.toLocaleString();\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport const commentCommand = new Command('comment')\n .description('Manage issue comments');\n\n// ---- list ----\ncommentCommand\n .command('list <identifier>')\n .description('List comments on an issue (UUID or PREFIX-NUMBER e.g. ATOLL-42)')\n .action(async (identifier: string) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n const data = await client.get<ListCommentsResponse>(\n `/api/orgs/${orgId}/issues/${issueId}/comments`,\n );\n\n // JSON mode: NDJSON\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const comment of data.comments) {\n process.stdout.write(JSON.stringify(comment) + '\\n');\n }\n return;\n }\n\n // TTY: human-readable\n if (data.comments.length === 0) {\n console.log('No comments found.');\n return;\n }\n\n for (const comment of data.comments) {\n const author = formatAuthor(comment);\n const time = formatTimestamp(comment.created_at);\n console.log(`${BOLD}${CYAN}${author}${RESET} ${DIM}${time}${RESET}`);\n console.log(comment.body);\n console.log();\n }\n console.log(`${DIM}${data.comments.length} comment(s)${RESET}`);\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- add ----\ncommentCommand\n .command('add <identifier>')\n .description('Add a comment to an issue')\n .requiredOption('--body <text>', 'Comment body')\n .action(async (identifier: string, opts: { body: string }) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, identifier);\n\n const { comment } = await client.post<SingleCommentResponse>(\n `/api/orgs/${orgId}/issues/${issueId}/comments`,\n { body: opts.body },\n );\n\n output(\n { comment },\n `✓ Comment added to ${identifier}`,\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- update ----\ncommentCommand\n .command('update <comment-id>')\n .description('Update a comment')\n .requiredOption('--body <text>', 'New comment body')\n .requiredOption('--issue <identifier>', 'Issue identifier (e.g. UUID or PREFIX-NUMBER)')\n .action(async (commentId: string, opts: { body: string; issue: string }) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, opts.issue);\n\n const { comment } = await client.patch<SingleCommentResponse>(\n `/api/orgs/${orgId}/issues/${issueId}/comments/${commentId}`,\n { body: opts.body },\n );\n\n output(\n { comment },\n `✓ Comment ${commentId} updated`,\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- delete ----\ncommentCommand\n .command('delete <comment-id>')\n .description('Delete a comment')\n .requiredOption('--issue <identifier>', 'Issue identifier (e.g. UUID or PREFIX-NUMBER)')\n .action(async (commentId: string, opts: { issue: string }) => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n const issueId = await resolveIssueId(client, orgId, opts.issue);\n\n await client.delete(`/api/orgs/${orgId}/issues/${issueId}/comments/${commentId}`);\n\n output(\n { success: true, id: commentId },\n `✓ Comment ${commentId} deleted`,\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { resolveOrg, handleApiError } from './issue.js';\nimport { bold, dim, cyan, green, success } from '../lib/colors.js';\nimport { buildProjectUrl } from '../lib/urls.js';\n\ninterface Project {\n id: string;\n name: string;\n slug?: string | null;\n description?: string | null;\n status: 'active' | 'archived';\n color: string;\n icon: string;\n issueCount?: number;\n completedCount?: number;\n progress?: number;\n created_at?: string;\n}\n\nfunction attachProjectUrl<T extends Project>(project: T, baseUrl: string, orgSlug: string): T & { url: string | null } {\n return { ...project, url: buildProjectUrl(baseUrl, orgSlug, project.slug) };\n}\n\ninterface Issue {\n id: string;\n title: string;\n status: string;\n priority: number;\n number?: number | null;\n}\n\nfunction progressBar(progress: number, width = 20): string {\n const filled = Math.round((progress / 100) * width);\n const empty = width - filled;\n return `[${'█'.repeat(filled)}${'░'.repeat(empty)}] ${progress}%`;\n}\n\nexport const projectCommand = new Command('project')\n .description('Manage projects')\n .addHelpText('after', `\nExamples:\n $ atoll project list\n $ atoll project create --name \"My Project\" --icon 🚀\n $ atoll project view <project-id>`);\n\n// ── atoll project list ──────────────────────────────────────────────────────\n\nprojectCommand\n .command('list')\n .description('List all projects with progress')\n .action(async () => {\n const client = new AtollClient();\n try {\n const org = await resolveOrg(client);\n const data = await client.get<{ projects: Project[] }>(`/api/orgs/${org.id}/projects`);\n const projects = (data.projects ?? []).map((p) => attachProjectUrl(p, client.baseUrl, org.slug));\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const p of projects) {\n process.stdout.write(JSON.stringify(p) + '\\n');\n }\n return;\n }\n\n if (projects.length === 0) {\n console.log(dim('No projects found.'));\n return;\n }\n\n for (const p of projects) {\n const progress = p.progress ?? 0;\n const bar = progressBar(progress);\n console.log(`${bold(`${p.icon} ${p.name}`)} ${dim(`(${p.id})`)}`);\n if (p.description) console.log(` ${dim(p.description)}`);\n console.log(` ${cyan(bar)} ${p.issueCount ?? 0} issues, ${p.completedCount ?? 0} done`);\n if (p.url) console.log(` ${dim(p.url)}`);\n if (p.status === 'archived') console.log(` ${dim('[archived]')}`);\n console.log('');\n }\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ── atoll project create ────────────────────────────────────────────────────\n\nprojectCommand\n .command('create')\n .description('Create a new project')\n .requiredOption('--name <name>', 'Project name')\n .option('--description <desc>', 'Project description')\n .option('--color <color>', 'Project color (hex)', '#6366f1')\n .option('--icon <icon>', 'Project icon (emoji)', '📁')\n .action(async (opts) => {\n const client = new AtollClient();\n try {\n const org = await resolveOrg(client);\n const data = await client.post<{ project: Project }>(`/api/orgs/${org.id}/projects`, {\n name: opts.name,\n description: opts.description ?? null,\n color: opts.color,\n icon: opts.icon,\n });\n const project = attachProjectUrl(data.project, client.baseUrl, org.slug);\n\n output(\n { project },\n success(`Created project ${project.icon} ${project.name} (${dim(project.id)})${project.url ? `\\n${project.url}` : ''}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ── atoll project view ──────────────────────────────────────────────────────\n\nprojectCommand\n .command('view <projectId>')\n .description('View project details and issues')\n .action(async (projectId) => {\n const client = new AtollClient();\n try {\n const org = await resolveOrg(client);\n const data = await client.get<{ project: Project & { issues: Issue[] } }>(\n `/api/orgs/${org.id}/projects/${projectId}`,\n );\n const p = attachProjectUrl(data.project, client.baseUrl, org.slug);\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n process.stdout.write(JSON.stringify(p) + '\\n');\n return;\n }\n\n const progress = p.progress ?? 0;\n console.log(bold(`${p.icon} ${p.name}`));\n if (p.description) console.log(` ${p.description}`);\n if (p.url) console.log(` ${dim(p.url)}`);\n console.log(` ${cyan(progressBar(progress))}`);\n console.log(` ${(p.issueCount ?? 0)} issues · ${(p.completedCount ?? 0)} completed`);\n console.log('');\n\n const issues: Issue[] = (p as unknown as { issues?: Issue[] }).issues ?? [];\n if (issues.length === 0) {\n console.log(dim('No issues in this project.'));\n return;\n }\n\n console.log(bold('Issues'));\n for (const issue of issues) {\n const num = issue.number ? `#${issue.number}` : issue.id.slice(0, 8);\n console.log(` ${dim(num)} ${issue.title} ${dim(`[${issue.status}]`)}`);\n }\n } catch (err) {\n handleApiError(err);\n }\n });\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { resolveOrgId, handleApiError } from './issue.js';\nimport { bold, dim, cyan, red, success } from '../lib/colors.js';\n\ninterface Milestone {\n id: string;\n name: string;\n description?: string | null;\n due_date?: string | null;\n status: 'active' | 'closed';\n issueCount?: number;\n completedCount?: number;\n progress?: number;\n isOverdue?: boolean;\n}\n\nfunction progressBar(progress: number, width = 20): string {\n const filled = Math.round((progress / 100) * width);\n const empty = width - filled;\n return `[${'█'.repeat(filled)}${'░'.repeat(empty)}] ${progress}%`;\n}\n\nexport const milestoneCommand = new Command('milestone')\n .description('Manage milestones')\n .addHelpText('after', `\nExamples:\n $ atoll milestone list --project <project-id>\n $ atoll milestone create --project <project-id> --name \"v1.0\" --date 2026-06-01`);\n\n// ── atoll milestone list ────────────────────────────────────────────────────\n\nmilestoneCommand\n .command('list')\n .description('List milestones for a project')\n .requiredOption('--project <id>', 'Project ID')\n .action(async (opts) => {\n const client = new AtollClient();\n try {\n const orgId = await resolveOrgId(client);\n const data = await client.get<{ milestones: Milestone[] }>(\n `/api/orgs/${orgId}/projects/${opts.project}/milestones`,\n );\n const milestones = data.milestones ?? [];\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const m of milestones) {\n process.stdout.write(JSON.stringify(m) + '\\n');\n }\n return;\n }\n\n if (milestones.length === 0) {\n console.log(dim('No milestones found.'));\n return;\n }\n\n for (const m of milestones) {\n const progress = m.progress ?? 0;\n const bar = progressBar(progress);\n const overdue = m.isOverdue ? ` ${red('[OVERDUE]')}` : '';\n console.log(`${bold(m.name)} ${dim(`(${m.id})`)}${overdue}`);\n if (m.due_date) console.log(` Due: ${m.due_date}`);\n if (m.description) console.log(` ${dim(m.description)}`);\n console.log(` ${cyan(bar)} ${m.issueCount ?? 0} issues, ${m.completedCount ?? 0} done`);\n if (m.status === 'closed') console.log(` ${dim('[closed]')}`);\n console.log('');\n }\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ── atoll milestone create ──────────────────────────────────────────────────\n\nmilestoneCommand\n .command('create')\n .description('Create a new milestone')\n .requiredOption('--project <id>', 'Project ID')\n .requiredOption('--name <name>', 'Milestone name')\n .option('--date <YYYY-MM-DD>', 'Due date')\n .option('--description <desc>', 'Description')\n .action(async (opts) => {\n const client = new AtollClient();\n try {\n const orgId = await resolveOrgId(client);\n const data = await client.post<{ milestone: Milestone }>(\n `/api/orgs/${orgId}/projects/${opts.project}/milestones`,\n {\n name: opts.name,\n description: opts.description ?? null,\n dueDate: opts.date ?? null,\n },\n );\n\n output(\n { milestone: data.milestone },\n success(`Milestone created: ${bold(data.milestone.name)} ${dim(`(${data.milestone.id})`)}`),\n );\n if (data.milestone.due_date) {\n console.log(` Due: ${data.milestone.due_date}`);\n }\n } catch (err) {\n handleApiError(err);\n }\n });\n","import { Command } from 'commander';\nimport { ensureProfile, readConfig, resolveConfig, writeConfig } from '../lib/config.js';\nimport { output } from '../lib/output.js';\nimport { success, bold, dim, gray } from '../lib/colors.js';\n\nexport const configCommand = new Command('config')\n .description('Manage CLI configuration (org, team, API key)')\n .addHelpText('after', `\nExamples:\n $ atoll config show\n $ atoll config set-org my-org\n $ atoll config set-team team-abc123\n $ atoll config set-base-url https://atollhq.com`);\n\n// ── atoll config show ───────────────────────────────────────────────────────\n\nconfigCommand\n .command('show')\n .description('Display current configuration')\n .option('--profile <name>', 'Profile name to show')\n .action((opts: { profile?: string }) => {\n const cfg = readConfig();\n const activeProfile = cfg.activeProfile;\n const resolved = resolveConfig({ profile: opts.profile });\n const apiKeySet = !!resolved.apiKey;\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n output(\n {\n activeProfile: activeProfile ?? null,\n selectedProfile: resolved.profile ?? null,\n orgSlug: resolved.orgSlug ?? null,\n defaultTeam: resolved.defaultTeam ?? null,\n baseUrl: resolved.baseUrl ?? null,\n apiKeySet,\n profiles: Object.keys(cfg.profiles ?? {}).sort(),\n },\n '',\n );\n return;\n }\n\n console.log(`${bold('Atoll CLI Configuration')}`);\n console.log(` ${dim('Active profile:')} ${activeProfile ? bold(activeProfile) : gray('(not set)')}`);\n console.log(` ${dim('Selected profile:')} ${resolved.profile ? bold(resolved.profile) : gray('(legacy config)')}`);\n console.log(` ${dim('Org slug:')} ${resolved.orgSlug ? bold(resolved.orgSlug) : gray('(not set)')}`);\n console.log(` ${dim('Default team:')} ${resolved.defaultTeam ? bold(resolved.defaultTeam) : gray('(not set)')}`);\n console.log(` ${dim('Base URL:')} ${resolved.baseUrl ? bold(resolved.baseUrl) : gray('(default)')}`);\n console.log(` ${dim('API key:')} ${apiKeySet ? bold('set') : gray('not set — run `atoll auth login`')}`);\n });\n\n// ── atoll config set-org ────────────────────────────────────────────────────\n\nconfigCommand\n .command('set-org <slug>')\n .description('Set the default organisation slug')\n .option('--profile <name>', 'Profile name to update')\n .action((slug: string, opts: { profile?: string }) => {\n const cfg = readConfig();\n const profileName = opts.profile || process.env.ATOLL_PROFILE || cfg.activeProfile;\n if (profileName) {\n ensureProfile(cfg, profileName).orgSlug = slug;\n } else {\n cfg.orgSlug = slug;\n }\n writeConfig(cfg);\n\n output(\n { orgSlug: slug, profile: profileName ?? null },\n profileName\n ? success(`Default org for profile \"${profileName}\" set to \"${slug}\"`)\n : success(`Default org set to \"${slug}\"`),\n );\n });\n\n// ── atoll config set-team ───────────────────────────────────────────────────\n\nconfigCommand\n .command('set-team <team>')\n .description('Set the default team slug or ID')\n .option('--profile <name>', 'Profile name to update')\n .action((team: string, opts: { profile?: string }) => {\n const cfg = readConfig();\n const profileName = opts.profile || process.env.ATOLL_PROFILE || cfg.activeProfile;\n if (profileName) {\n ensureProfile(cfg, profileName).defaultTeam = team;\n } else {\n cfg.defaultTeam = team;\n }\n writeConfig(cfg);\n\n output(\n { defaultTeam: team, profile: profileName ?? null },\n profileName\n ? success(`Default team for profile \"${profileName}\" set to \"${team}\"`)\n : success(`Default team set to \"${team}\"`),\n );\n });\n\n// ── atoll config set-base-url ───────────────────────────────────────────────\n\nconfigCommand\n .command('set-base-url <url>')\n .description('Set the default API base URL')\n .option('--profile <name>', 'Profile name to update')\n .action((url: string, opts: { profile?: string }) => {\n const cfg = readConfig();\n const profileName = opts.profile || process.env.ATOLL_PROFILE || cfg.activeProfile;\n if (profileName) {\n ensureProfile(cfg, profileName).baseUrl = url;\n } else {\n cfg.baseUrl = url;\n }\n writeConfig(cfg);\n\n output(\n { baseUrl: url, profile: profileName ?? null },\n profileName\n ? success(`Base URL for profile \"${profileName}\" set to \"${url}\"`)\n : success(`Base URL set to \"${url}\"`),\n );\n });\n","import { Command } from 'commander';\nimport { AtollClient } from '../lib/client.js';\nimport { output, outputError } from '../lib/output.js';\nimport { bold, dim, green, gray, success } from '../lib/colors.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface Webhook {\n id: string;\n url: string;\n events: string[];\n enabled: boolean;\n created_at: string;\n}\n\ninterface WebhookListResponse {\n webhooks: Webhook[];\n}\n\ninterface WebhookCreateResponse {\n webhook: Webhook;\n secret: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction handleApiError(err: unknown): never {\n const msg = (err as Error).message ?? String(err);\n if (/API 401\\b/.test(msg)) {\n outputError('Session expired — run `atoll auth login` to re-authenticate.');\n } else if (/API 403\\b/.test(msg)) {\n outputError('Permission denied. You do not have access to this resource.');\n } else {\n outputError(msg);\n }\n process.exit(1);\n}\n\nasync function resolveOrgId(client: AtollClient, orgSlugOverride?: string): Promise<string> {\n const { orgs } = await client.get<{ orgs: { id: string; slug: string }[] }>('/api/orgs');\n if (!orgs || orgs.length === 0) {\n outputError('No organizations found.');\n process.exit(1);\n }\n if (orgs.length === 1) return orgs[0].id;\n\n const { resolveConfig } = await import('../lib/config.js');\n const config = resolveConfig();\n const slug = orgSlugOverride ?? config.orgSlug;\n\n if (slug) {\n const match = orgs.find((o) => o.slug === slug);\n if (match) return match.id;\n outputError(`Org \"${slug}\" not found. Available: ${orgs.map((o) => o.slug).join(', ')}`);\n process.exit(1);\n }\n outputError(`Multiple orgs found. Use --org <slug>.`);\n process.exit(1);\n}\n\nfunction padEnd(str: string, len: number): string {\n return str.length >= len ? str.slice(0, len) : str + ' '.repeat(len - str.length);\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport const webhookCommand = new Command('webhook')\n .description('Manage outbound webhooks')\n .addHelpText('after', `\nExamples:\n $ atoll webhook list\n $ atoll webhook create --url https://example.com/hook --events issue.created,issue.updated\n $ atoll webhook delete <id>`);\n\n// ---- list ----\nwebhookCommand\n .command('list')\n .description('List webhooks for the current org')\n .action(async () => {\n try {\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n\n const data = await client.get<WebhookListResponse>(`/api/webhooks?orgId=${orgId}`);\n\n if (!process.stdout.isTTY || process.env.OUTPUT_FORMAT === 'json') {\n for (const wh of data.webhooks) {\n process.stdout.write(JSON.stringify(wh) + '\\n');\n }\n return;\n }\n\n if (data.webhooks.length === 0) {\n console.log('No webhooks configured.');\n return;\n }\n\n const header = bold(`${padEnd('ID', 38)} ${padEnd('URL', 40)} ${padEnd('EVENTS', 20)} ACTIVE`);\n console.log(header);\n console.log('─'.repeat(100));\n for (const wh of data.webhooks) {\n const evts = wh.events.length === 0 ? 'all' : wh.events.join(',');\n const active = wh.enabled ? green('yes') : gray('no');\n const url = wh.url.length > 38 ? wh.url.slice(0, 35) + '…' : wh.url;\n const evtStr = evts.length > 18 ? evts.slice(0, 15) + '…' : evts;\n console.log(`${padEnd(wh.id.slice(0, 36), 38)} ${padEnd(url, 40)} ${padEnd(evtStr, 20)} ${active}`);\n }\n console.log(dim(`${data.webhooks.length} webhook(s)`));\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- create ----\nwebhookCommand\n .command('create')\n .description('Create a new webhook')\n .requiredOption('--url <url>', 'Payload URL (HTTPS)')\n .option('--events <events>', 'Comma-separated event types (default: all)', '')\n .action(async (opts: { url: string; events: string }) => {\n try {\n if (!opts.url.startsWith('https://')) {\n outputError('URL must use HTTPS.');\n process.exit(2);\n }\n\n const events = opts.events\n ? opts.events.split(',').map((e) => e.trim()).filter(Boolean)\n : [];\n\n const client = new AtollClient();\n const orgId = await resolveOrgId(client);\n\n const data = await client.post<WebhookCreateResponse>(\n `/api/webhooks?orgId=${orgId}`,\n { url: opts.url, events },\n );\n\n output(\n { webhook: data.webhook, secret: data.secret },\n [\n success(`Created webhook ${data.webhook.id}`),\n '',\n bold('Signing secret (save this — shown only once):'),\n ` ${data.secret}`,\n ].join('\\n'),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n\n// ---- delete ----\nwebhookCommand\n .command('delete <id>')\n .description('Delete a webhook')\n .action(async (id: string) => {\n try {\n const client = new AtollClient();\n await client.delete(`/api/webhooks/${id}`);\n\n output(\n { success: true, id },\n success(`Deleted webhook ${id}`),\n );\n } catch (err) {\n handleApiError(err);\n }\n });\n"],"mappings":";;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BO,SAAS,aAA0B;AACxC,MAAI,KAAC,2BAAW,WAAW,EAAG,QAAO,CAAC;AACtC,MAAI;AACF,WAAO,KAAK,UAAM,6BAAa,aAAa,OAAO,CAAC;AAAA,EACtD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,YAAY,QAA2B;AACrD,MAAI,KAAC,2BAAW,UAAU,GAAG;AAC3B,kCAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,EAC3C;AACA,oCAAc,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AAC5E;AAEO,SAAS,eAAqB;AACnC,UAAI,2BAAW,WAAW,GAAG;AAC3B,mCAAW,WAAW;AAAA,EACxB;AACF;AAEO,SAAS,iBAAiB,SAAS,WAAW,GAAuB;AAC1E,SAAO,QAAQ,IAAI,iBAAiB,OAAO;AAC7C;AAEO,SAAS,WAAW,QAAqB,aAAgD;AAC9F,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,OAAO,WAAW,WAAW;AACtC;AAEO,SAAS,cAAc,QAAqB,aAAmC;AACpF,SAAO,aAAa,CAAC;AACrB,SAAO,SAAS,WAAW,MAAM,CAAC;AAClC,SAAO,OAAO,SAAS,WAAW;AACpC;AAEO,SAAS,cAAc,MAA6C;AACzE,QAAM,SAAS,WAAW;AAC1B,QAAM,cAAc,MAAM,WAAW,iBAAiB,MAAM;AAC5D,QAAM,UAAU,WAAW,QAAQ,WAAW;AAE9C,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ,QAAQ,IAAI,iBAAiB,SAAS,WAAW,cAAc,SAAY,OAAO;AAAA,IAC1F,SAAS,QAAQ,IAAI,aAAa,SAAS,YAAY,cAAc,SAAY,OAAO;AAAA,IACxF,aAAa,QAAQ,IAAI,cAAc,SAAS,gBAAgB,cAAc,SAAY,OAAO;AAAA,IACjG,SAAS,QAAQ,IAAI,kBAAkB,SAAS,YAAY,cAAc,SAAY,OAAO;AAAA,EAC/F;AACF;AAEO,SAAS,UAAU,MAAiD;AACzE,SAAO,cAAc,IAAI,EAAE;AAC7B;AAhFA,oBACA,gBACA,kBAsBM,YACA;AAzBN;AAAA;AAAA;AAAA,qBAA+E;AAC/E,qBAAwB;AACxB,uBAAqB;AAsBrB,IAAM,iBAAa,2BAAK,wBAAQ,GAAG,QAAQ;AAC3C,IAAM,kBAAc,uBAAK,YAAY,aAAa;AAAA;AAAA;;;ACzBlD,IAAAA,oBAAwB;;;ACAxB,uBAAwB;AACxB;;;ACDA;;;ACEA,SAAS,aAAsB;AAC7B,MAAI,QAAQ,IAAI,kBAAkB,OAAQ,QAAO;AACjD,SAAO,CAAC,QAAQ,OAAO;AACzB;AAEO,SAAS,OAAO,MAAoB,eAA6B;AACtE,MAAI,WAAW,GAAG;AAChB,YAAQ,OAAO,MAAM,KAAK,UAAU,IAAI,IAAI,IAAI;AAAA,EAClD,OAAO;AACL,YAAQ,IAAI,aAAa;AAAA,EAC3B;AACF;AAEO,SAAS,YAAY,SAAuB;AACjD,MAAI,WAAW,GAAG;AAChB,YAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,OAAO,QAAQ,CAAC,IAAI,IAAI;AAAA,EAChE,OAAO;AACL,YAAQ,MAAM,UAAU,OAAO,EAAE;AAAA,EACnC;AACF;;;ADlBA,IAAM,mBAAmB;AAElB,IAAM,cAAN,MAAkB;AAAA,EAIvB,YAAY,MAAgE;AAC1E,UAAM,SAAS,cAAc,EAAE,SAAS,MAAM,QAAQ,CAAC;AACvD,SAAK,UAAU,MAAM,WAAW,OAAO,WAAW;AAElD,UAAM,MAAM,MAAM,UAAU,OAAO;AACnC,QAAI,CAAC,KAAK;AACR,YAAM,SAAS,OAAO,UAAU,iBAAiB,OAAO,OAAO,MAAM;AACrE,kBAAY,mBAAmB,MAAM,kEAAkE;AACvG,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,QAAqB,MAAc,MAAgC;AACvE,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,GAAG;AAAA,MACH,SAAS;AAAA,QACP,iBAAiB,UAAU,KAAK,MAAM;AAAA,QACtC,gBAAgB;AAAA,QAChB,GAAG,MAAM;AAAA,MACX;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,YAAM,IAAI,MAAM,OAAO,IAAI,MAAM,KAAK,IAAI,EAAE;AAAA,IAC9C;AAEA,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA,EAEA,MAAM,IAAiB,MAA0B;AAC/C,WAAO,KAAK,QAAW,MAAM,EAAE,QAAQ,MAAM,CAAC;AAAA,EAChD;AAAA,EAEA,MAAM,KAAkB,MAAc,MAA4B;AAChE,WAAO,KAAK,QAAW,MAAM;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAmB,MAAc,MAA4B;AACjE,WAAO,KAAK,QAAW,MAAM;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAoB,MAA0B;AAClD,WAAO,KAAK,QAAW,MAAM,EAAE,QAAQ,SAAS,CAAC;AAAA,EACnD;AACF;;;ADnDO,IAAM,cAAc,IAAI,yBAAQ,MAAM,EAC1C,YAAY,uBAAuB;AAEtC,YACG,QAAQ,OAAO,EACf,YAAY,yCAAyC,EACrD,eAAe,mBAAmB,kBAAkB,EACpD,OAAO,oBAAoB,0CAA0C,EACrE,OAAO,gBAAgB,mCAAmC,EAC1D,OAAO,iBAAiB,0CAA0C,EAClE,OAAO,oBAAoB,2BAA2B,EACtD,OAAO,CAAC,SAA2F;AAClG,QAAM,SAAS,WAAW;AAC1B,QAAM,cAAc,KAAK,WAAW,QAAQ,IAAI,iBAAiB,OAAO;AACxE,QAAM,UAAU,KAAK,OAAO,QAAQ,IAAI;AACxC,QAAM,cAAc,KAAK,QAAQ,QAAQ,IAAI;AAC7C,QAAM,UAAU,KAAK;AAErB,MAAI,aAAa;AACf,UAAM,UAAU,cAAc,QAAQ,WAAW;AACjD,YAAQ,SAAS,KAAK;AACtB,QAAI,YAAY,OAAW,SAAQ,UAAU;AAC7C,QAAI,gBAAgB,OAAW,SAAQ,cAAc;AACrD,QAAI,YAAY,OAAW,SAAQ,UAAU;AAC7C,WAAO,gBAAgB;AAAA,EACzB,OAAO;AACL,WAAO,SAAS,KAAK;AACrB,QAAI,YAAY,OAAW,QAAO,UAAU;AAC5C,QAAI,gBAAgB,OAAW,QAAO,cAAc;AACpD,QAAI,YAAY,OAAW,QAAO,UAAU;AAAA,EAC9C;AAEA,cAAY,MAAM;AAClB;AAAA,IACE,EAAE,QAAQ,MAAM,SAAS,iBAAiB,SAAS,eAAe,KAAK;AAAA,IACvE,cACI,oCAA+B,WAAW,8BAC1C;AAAA,EACN;AACF,CAAC;AAEH,YACG,QAAQ,QAAQ,EAChB,YAAY,wCAAwC,EACpD,OAAO,oBAAoB,uBAAuB,EAClD,OAAO,OAAO,SAA+B;AAC5C,QAAM,WAAW,cAAc,EAAE,SAAS,KAAK,QAAQ,CAAC;AACxD,QAAM,SAAS,SAAS;AACxB,MAAI,CAAC,QAAQ;AACX,UAAM,SAAS,SAAS,UAAU,iBAAiB,SAAS,OAAO,MAAM;AACzE,gBAAY,oBAAoB,MAAM,kEAAkE;AACxG,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACF,UAAM,SAAS,IAAI,YAAY,EAAE,QAAQ,SAAS,SAAS,SAAS,SAAS,KAAK,QAAQ,CAAC;AAC3F,UAAM,KAAK,MAAM,OAAO,IAGrB,cAAc;AAEjB;AAAA,MACE,EAAE,QAAQ,iBAAiB,GAAG,GAAG;AAAA,MACjC;AAAA,QACE;AAAA,QACA,SAAS,UAAU,cAAc,SAAS,OAAO,KAAK;AAAA,QACtD,GAAG,MAAM,OAAO,WAAW,GAAG,KAAK,IAAI,KAAK;AAAA,QAC5C,GAAG,MAAM,QAAQ,YAAY,GAAG,KAAK,KAAK,KAAK;AAAA,QAC/C,GAAG,KAAK,OAAO,UAAU,GAAG,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,MAAM;AAAA,MAC5D,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AAAA,IACd;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,MAAO,IAAc,WAAW;AACtC,QAAI,iBAAiB,KAAK,GAAG,GAAG;AAC9B,kBAAY,+BAA+B;AAC3C,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,gBAAY,sBAAsB,GAAG,EAAE;AACvC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,YACG,QAAQ,UAAU,EAClB,YAAY,0BAA0B,EACtC,OAAO,MAAM;AACZ,QAAM,SAAS,WAAW;AAC1B,QAAM,gBAAgB,OAAO;AAC7B,QAAM,WAAW,OAAO,QAAQ,OAAO,YAAY,CAAC,CAAC,EAClD,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,EACrC,IAAI,CAAC,CAAC,MAAM,OAAO,OAAO;AAAA,IACzB;AAAA,IACA,QAAQ,SAAS;AAAA,IACjB,WAAW,CAAC,CAAC,QAAQ;AAAA,IACrB,SAAS,QAAQ,WAAW;AAAA,IAC5B,aAAa,QAAQ,eAAe;AAAA,IACpC,SAAS,QAAQ,WAAW;AAAA,EAC9B,EAAE;AAEJ,MAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ;AACjE,WAAO,EAAE,eAAe,iBAAiB,MAAM,SAAS,GAAG,EAAE;AAC7D;AAAA,EACF;AAEA,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAI,yBAAyB;AACrC;AAAA,EACF;AAEA,aAAW,WAAW,UAAU;AAC9B,UAAM,SAAS,QAAQ,SAAS,MAAM;AACtC,UAAM,QAAQ;AAAA,MACZ,GAAG,MAAM,IAAI,QAAQ,IAAI;AAAA,MACzB,QAAQ,YAAY,YAAY;AAAA,MAChC,QAAQ,UAAU,OAAO,QAAQ,OAAO,KAAK;AAAA,MAC7C,QAAQ,cAAc,QAAQ,QAAQ,WAAW,KAAK;AAAA,MACtD,QAAQ,UAAU,WAAW,QAAQ,OAAO,KAAK;AAAA,IACnD,EAAE,OAAO,OAAO;AAChB,YAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;AAAA,EAC9B;AACF,CAAC;AAEH,YACG,QAAQ,eAAe,EACvB,YAAY,6BAA6B,EACzC,OAAO,CAAC,gBAAwB;AAC/B,QAAM,SAAS,WAAW;AAC1B,MAAI,CAAC,WAAW,QAAQ,WAAW,GAAG;AACpC,gBAAY,YAAY,WAAW,iDAAiD,WAAW,2BAA2B;AAC1H,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,gBAAgB;AACvB,cAAY,MAAM;AAClB;AAAA,IACE,EAAE,eAAe,YAAY;AAAA,IAC7B,iCAA4B,WAAW;AAAA,EACzC;AACF,CAAC;AAEH,YACG,QAAQ,QAAQ,EAChB,YAAY,uBAAuB,EACnC,OAAO,oBAAoB,yBAAyB,EACpD,OAAO,CAAC,SAA+B;AAEtC,QAAM,SAAS,WAAW;AAC1B,QAAM,cAAc,KAAK,WAAW,QAAQ,IAAI,iBAAiB,OAAO;AAExE,MAAI,aAAa;AACf,UAAM,UAAU,WAAW,QAAQ,WAAW;AAC9C,QAAI,CAAC,SAAS;AACZ,kBAAY,YAAY,WAAW,cAAc;AACjD,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,WAAO,QAAQ;AAAA,EACjB,OAAO;AACL,WAAO,OAAO;AAAA,EAChB;AAEA,cAAY,MAAM;AAClB;AAAA,IACE,EAAE,QAAQ,MAAM,SAAS,cAAc,SAAS,eAAe,KAAK;AAAA,IACpE,cACI,iCAA4B,WAAW,yDACvC;AAAA,EACN;AACF,CAAC;;;AGpLH,IAAAC,oBAAwB;;;ACKxB,SAAS,QAAiB;AACxB,SAAO,QAAQ,OAAO,UAAU,QAAQ,QAAQ,IAAI,kBAAkB;AACxE;AAEA,SAAS,KAAK,MAAc,MAAsB;AAChD,MAAI,CAAC,MAAM,EAAG,QAAO;AACrB,SAAO,QAAQ,IAAI,IAAI,IAAI;AAC7B;AAGO,IAAM,OAAO,CAAC,MAAc,KAAK,KAAK,CAAC;AACvC,IAAM,MAAM,CAAC,MAAc,KAAK,KAAK,CAAC;AACtC,IAAM,MAAM,CAAC,MAAc,KAAK,MAAM,CAAC;AACvC,IAAM,QAAQ,CAAC,MAAc,KAAK,MAAM,CAAC;AACzC,IAAM,SAAS,CAAC,MAAc,KAAK,MAAM,CAAC;AAE1C,IAAM,OAAO,CAAC,MAAc,KAAK,MAAM,CAAC;AACxC,IAAM,OAAO,CAAC,MAAc,KAAK,MAAM,CAAC;AAIxC,SAAS,YAAY,QAAgB,MAAsB;AAChE,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAe,aAAO,MAAM,IAAI;AAAA,IACrC,KAAK;AAAe,aAAO,OAAO,IAAI;AAAA,IACtC,KAAK;AAAA,IACL,KAAK;AAAe,aAAO,KAAK,IAAI;AAAA,IACpC,KAAK;AAAe,aAAO,IAAI,IAAI;AAAA,IACnC;AAAoB,aAAO;AAAA,EAC7B;AACF;AAGO,SAAS,aAAa,UAA0B;AACrD,UAAQ,UAAU;AAAA,IAChB,KAAK;AAAG,aAAO,IAAI,oBAAK;AAAA;AAAA,IACxB,KAAK;AAAG,aAAO,OAAO,oBAAK;AAAA;AAAA,IAC3B,KAAK;AAAG,aAAO,KAAK,cAAI;AAAA;AAAA,IACxB,KAAK;AAAG,aAAO,IAAI,KAAK;AAAA;AAAA,IACxB;AAAS,aAAO,OAAO,QAAQ;AAAA,EACjC;AACF;AAGO,IAAM,UAAU,CAAC,QAAgB,MAAM,UAAK,GAAG,EAAE;AAIjD,IAAM,QAAQ,MAAM,IAAI,YAAY;AACpC,IAAM,OAAO,MAAM,IAAI,YAAY;AACnC,IAAM,MAAM,MAAM,IAAI,YAAY;;;AChDlC,SAAS,aAAa,MAAsB;AACjD,QAAM,UAAU,KAAK,QAAQ,mBAAmB,GAAG,EAAE,KAAK;AAC1D,QAAM,QAAQ,QAAQ,MAAM,KAAK,EAAE,OAAO,OAAO;AAEjD,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,MAAI,MAAM,UAAU,GAAG;AACrB,WAAO,MACJ,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,EACf,KAAK,EAAE,EACP,YAAY;AAAA,EACjB;AAEA,QAAM,OAAO,MAAM,CAAC;AACpB,MAAI,KAAK,UAAU,EAAG,QAAO,KAAK,YAAY;AAC9C,SAAO,KAAK,MAAM,GAAG,CAAC,EAAE,YAAY;AACtC;AAWA,SAAS,mBAAmB,SAAyB;AACnD,SAAO,QAAQ,SAAS,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI;AACxD;AAEO,SAAS,cACd,SACA,SACA,aACA,aACe;AAKf,MAAI,CAAC,WAAW,CAAC,eAAe,eAAe,KAAM,QAAO;AAC5D,SAAO,GAAG,mBAAmB,OAAO,CAAC,IAAI,OAAO,aAAa,WAAW,WAAW,WAAW;AAChG;AAEO,SAAS,gBACd,SACA,SACA,aACe;AACf,MAAI,CAAC,WAAW,CAAC,YAAa,QAAO;AACrC,SAAO,GAAG,mBAAmB,OAAO,CAAC,IAAI,OAAO,aAAa,WAAW;AAC1E;;;AFNO,SAAS,eAAe,KAAqB;AAClD,QAAM,MAAO,IAAc,WAAW,OAAO,GAAG;AAChD,MAAI,YAAY,KAAK,GAAG,GAAG;AACzB,gBAAY,mEAA8D;AAAA,EAC5E,WAAW,YAAY,KAAK,GAAG,GAAG;AAChC,gBAAY,6DAA6D;AAAA,EAC3E,OAAO;AACL,gBAAY,GAAG;AAAA,EACjB;AACA,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,iBAAiB,CAAC,WAAW,QAAQ,eAAe,QAAQ,WAAW;AAC7E,IAAM,kBAA0C,EAAE,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM;AAEhG,eAAsB,WAAW,QAAqB,iBAAwC;AAC5F,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,IAAqB,WAAW;AAC9D,MAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,gBAAY,2CAA2C;AACvD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,KAAK,WAAW,EAAG,QAAO,KAAK,CAAC;AAGpC,QAAM,EAAE,eAAAC,eAAc,IAAI,MAAM;AAChC,QAAM,SAASA,eAAc;AAC7B,QAAM,OAAO,mBAAmB,OAAO;AAEvC,MAAI,MAAM;AACR,UAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAC9C,QAAI,MAAO,QAAO;AAClB,gBAAY,QAAQ,IAAI,2BAA2B,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AACvF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,cAAY,4FAA4F,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AAC5I,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAsB,aAAa,QAAqB,iBAA2C;AACjG,UAAQ,MAAM,WAAW,QAAQ,eAAe,GAAG;AACrD;AAMA,eAAsB,gBACpB,QACA,OACsC;AACtC,QAAM,EAAE,SAAS,IAAI,MAAM,OAAO;AAAA,IAChC,aAAa,KAAK;AAAA,EACpB;AACA,QAAM,MAAM,oBAAI,IAA4B;AAC5C,aAAW,KAAK,YAAY,CAAC,EAAG,KAAI,IAAI,EAAE,IAAI,CAAC;AAC/C,SAAO;AACT;AAMO,SAAS,eACd,OACA,SACA,SACA,UACc;AACd,QAAM,UAAU,MAAM,aAAa,SAAS,IAAI,MAAM,UAAU,IAAI;AACpE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,KAAK,cAAc,SAAS,SAAS,SAAS,MAAM,MAAM,UAAU,IAAI;AAAA,EAC1E;AACF;AAMA,eAAsB,eAAe,QAAqB,OAAe,YAAqC;AAE5G,MAAI,kEAAkE,KAAK,UAAU,GAAG;AACtF,WAAO;AAAA,EACT;AAIA,QAAM,QAAQ,WAAW,MAAM,2BAA2B;AAC1D,MAAI,CAAC,OAAO;AACV,gBAAY,wBAAwB,UAAU,iDAAiD;AAC/F,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,SAAS,MAAM,CAAC,GAAG,EAAE;AAGjC,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO;AAAA,IAC9B,aAAa,KAAK,kBAAkB,GAAG;AAAA,EACzC;AACA,MAAI,OAAO,SAAS,KAAK,OAAO,CAAC,EAAE,WAAW,KAAK;AACjD,WAAO,OAAO,CAAC,EAAE;AAAA,EACnB;AAGA,MAAI,SAAS;AACb,QAAM,WAAW;AACjB,SAAO,MAAM;AACX,UAAM,OAAO,MAAM,OAAO;AAAA,MACxB,aAAa,KAAK,iBAAiB,QAAQ,WAAW,MAAM;AAAA,IAC9D;AACA,UAAM,QAAQ,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG;AACtD,QAAI,MAAO,QAAO,MAAM;AACxB,cAAU;AACV,QAAI,UAAU,KAAK,MAAO;AAAA,EAC5B;AAEA,cAAY,UAAU,GAAG,aAAa;AACtC,UAAQ,KAAK,CAAC;AAChB;AAEA,SAAS,iBAAiB,OAAc,UAAgD;AACtF,MAAI,MAAM,UAAU,KAAM,QAAO,MAAM,GAAG,MAAM,GAAG,CAAC;AACpD,MAAI,YAAY,MAAM,YAAY;AAChC,UAAM,UAAU,SAAS,IAAI,MAAM,UAAU;AAC7C,QAAI,SAAS,KAAM,QAAO,GAAG,aAAa,QAAQ,IAAI,CAAC,IAAI,MAAM,MAAM;AAAA,EACzE;AACA,SAAO,SAAS,MAAM,MAAM;AAC9B;AAEA,SAAS,OAAO,KAAa,KAAqB;AAChD,SAAO,IAAI,UAAU,MAAM,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,OAAO,MAAM,IAAI,MAAM;AAClF;AAMO,IAAM,eAAe,IAAI,0BAAQ,OAAO,EAC5C,YAAY,eAAe,EAC3B,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+CAOuB;AAG/C,aACG,QAAQ,MAAM,EACd,YAAY,aAAa,EACzB,OAAO,qBAAqB,qBAAqB,eAAe,KAAK,IAAI,CAAC,GAAG,EAC7E,OAAO,qBAAqB,uBAAuB,EACnD,OAAO,kBAAkB,0DAA0D,QAAQ,EAC3F,OAAO,eAAe,uBAAuB,QAAQ,EACrD,OAAO,OAAO,SAAoF;AACjG,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,MAAM,MAAM,WAAW,MAAM;AAEnC,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,KAAK,OAAQ,QAAO,IAAI,UAAU,KAAK,MAAM;AACjD,QAAI,KAAK,SAAU,QAAO,IAAI,cAAc,KAAK,QAAQ;AACzD,QAAI,KAAK,aAAa,OAAW,QAAO,IAAI,YAAY,OAAO,KAAK,QAAQ,CAAC;AAC7E,QAAI,KAAK,UAAU,OAAW,QAAO,IAAI,SAAS,OAAO,KAAK,KAAK,CAAC;AAEpE,UAAM,KAAK,OAAO,SAAS;AAC3B,UAAM,CAAC,MAAM,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,MACzC,OAAO,IAAkB,aAAa,IAAI,EAAE,UAAU,KAAK,IAAI,EAAE,KAAK,EAAE,EAAE;AAAA,MAC1E,gBAAgB,QAAQ,IAAI,EAAE;AAAA,IAChC,CAAC;AAED,UAAM,WAAW,KAAK,OAAO;AAAA,MAAI,CAAC,UAChC,eAAe,OAAO,OAAO,SAAS,IAAI,MAAM,QAAQ;AAAA,IAC1D;AAGA,QAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ;AACjE,iBAAW,SAAS,UAAU;AAC5B,gBAAQ,OAAO,MAAM,KAAK,UAAU,KAAK,IAAI,IAAI;AAAA,MACnD;AACA;AAAA,IACF;AAGA,QAAI,SAAS,WAAW,GAAG;AACzB,cAAQ,IAAI,kBAAkB;AAC9B;AAAA,IACF;AAEA,UAAM,SAAS,KAAK,GAAG,OAAO,MAAM,EAAE,CAAC,IAAI,OAAO,UAAU,EAAE,CAAC,IAAI,OAAO,OAAO,CAAC,CAAC,QAAQ;AAC3F,YAAQ,IAAI,MAAM;AAClB,YAAQ,IAAI,SAAI,OAAO,EAAE,CAAC;AAC1B,eAAW,SAAS,UAAU;AAC5B,YAAM,KAAK,iBAAiB,OAAO,QAAQ;AAC3C,YAAM,MAAM,gBAAgB,MAAM,QAAQ,KAAK,OAAO,MAAM,QAAQ;AACpE,YAAM,QAAQ,MAAM,MAAM,SAAS,KAAK,MAAM,MAAM,MAAM,GAAG,EAAE,IAAI,WAAM,MAAM;AAC/E,YAAM,OAAO,aAAa,MAAM,QAAQ;AACxC,cAAQ,IAAI,GAAG,OAAO,IAAI,EAAE,CAAC,IAAI,YAAY,MAAM,QAAQ,OAAO,MAAM,QAAQ,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,KAAK,EAAE;AAAA,IAC3H;AACA,YAAQ,IAAI,IAAI,GAAG,SAAS,MAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AAAA,EAC/D,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,aACG,QAAQ,mBAAmB,EAC3B,YAAY,0DAA0D,EACtE,OAAO,OAAO,eAAuB;AACpC,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,MAAM,MAAM,WAAW,MAAM;AACnC,UAAM,UAAU,MAAM,eAAe,QAAQ,IAAI,IAAI,UAAU;AAE/D,UAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC9C,OAAO,IAAoB,aAAa,IAAI,EAAE,WAAW,OAAO,EAAE;AAAA,MAClE,gBAAgB,QAAQ,IAAI,EAAE;AAAA,IAChC,CAAC;AAED,UAAM,WAAW,eAAe,OAAO,OAAO,SAAS,IAAI,MAAM,QAAQ;AAGzE,QAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ;AACjE,cAAQ,OAAO,MAAM,KAAK,UAAU,QAAQ,IAAI,IAAI;AACpD;AAAA,IACF;AAGA,UAAM,KAAK,iBAAiB,UAAU,QAAQ;AAC9C,UAAM,MAAM,gBAAgB,SAAS,QAAQ,KAAK,OAAO,SAAS,QAAQ;AAC1E,YAAQ,IAAI,GAAG,KAAK,EAAE,CAAC,IAAI,SAAS,KAAK,EAAE;AAC3C,YAAQ,IAAI,WAAW,YAAY,SAAS,QAAQ,SAAS,MAAM,CAAC,eAAe,aAAa,SAAS,QAAQ,CAAC,IAAI,GAAG,EAAE;AAC3H,QAAI,SAAS,IAAK,SAAQ,IAAI,QAAQ,SAAS,GAAG,EAAE;AACpD,QAAI,SAAS,YAAa,SAAQ,IAAI,aAAa,SAAS,WAAW,EAAE;AACzE,QAAI,SAAS,SAAU,SAAQ,IAAI,QAAQ,SAAS,QAAQ,EAAE;AAC9D,QAAI,SAAS,aAAa;AACxB,cAAQ,IAAI;AAAA,EAAK,SAAS,WAAW,EAAE;AAAA,IACzC;AACA,QAAI,SAAS,gBAAgB,SAAS,aAAa,SAAS,GAAG;AAC7D,YAAM,SAAS,SAAS,aAAa,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE,KAAK,IAAI;AACxE,cAAQ,IAAI,WAAW,MAAM,EAAE;AAAA,IACjC;AACA,QAAI,SAAS,aAAa,MAAM,QAAQ,SAAS,SAAS,KAAK,SAAS,UAAU,SAAS,GAAG;AAC5F,cAAQ,IAAI;AAAA,YAAe,SAAS,UAAU,MAAM,EAAE;AAAA,IACxD;AACA,QAAI,SAAS,WAAY,SAAQ,IAAI,IAAI,YAAY,SAAS,UAAU,EAAE,CAAC;AAC3E,QAAI,SAAS,WAAY,SAAQ,IAAI,IAAI,YAAY,SAAS,UAAU,EAAE,CAAC;AAAA,EAC7E,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,aACG,QAAQ,QAAQ,EAChB,YAAY,oBAAoB,EAChC,eAAe,mBAAmB,aAAa,EAC/C,OAAO,wBAAwB,mBAAmB,EAClD,OAAO,qBAAqB,WAAW,eAAe,KAAK,IAAI,CAAC,GAAG,EACnE,OAAO,kBAAkB,gDAAgD,QAAQ,EACjF,OAAO,OAAO,SAAsF;AACnG,MAAI;AACF,QAAI,KAAK,UAAU,CAAC,eAAe,SAAS,KAAK,MAAuC,GAAG;AACzF,kBAAY,mBAAmB,KAAK,MAAM,sBAAsB,eAAe,KAAK,IAAI,CAAC,EAAE;AAC3F,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI,KAAK,aAAa,UAAa,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,SAAS,KAAK,QAAQ,GAAG;AACxE,kBAAY,gEAAgE;AAC5E,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,MAAM,MAAM,WAAW,MAAM;AAEnC,UAAM,OAAgC,EAAE,OAAO,KAAK,MAAM;AAC1D,QAAI,KAAK,gBAAgB,OAAW,MAAK,cAAc,KAAK;AAC5D,QAAI,KAAK,OAAQ,MAAK,SAAS,KAAK;AACpC,QAAI,KAAK,aAAa,OAAW,MAAK,WAAW,KAAK;AAEtD,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO,KAAqB,aAAa,IAAI,EAAE,WAAW,IAAI;AACtF,UAAM,WAAW,MAAM,gBAAgB,QAAQ,IAAI,EAAE;AACrD,UAAM,WAAW,eAAe,OAAO,OAAO,SAAS,IAAI,MAAM,QAAQ;AAEzE;AAAA,MACE,EAAE,OAAO,SAAS;AAAA,MAClB,QAAQ,WAAW,iBAAiB,UAAU,QAAQ,CAAC,KAAK,SAAS,KAAK,GAAG,SAAS,MAAM;AAAA,EAAK,SAAS,GAAG,KAAK,EAAE,EAAE;AAAA,IACxH;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,aACG,QAAQ,qBAAqB,EAC7B,YAAY,iBAAiB,EAC7B,OAAO,eAAe,WAAW,EACjC,OAAO,gBAAgB,eAAe,eAAe,KAAK,IAAI,CAAC,GAAG,EAClE,OAAO,kBAAkB,sBAAsB,QAAQ,EACvD,OAAO,OAAO,YAAoB,SAAiE;AAClG,MAAI;AACF,QAAI,KAAK,UAAU,CAAC,eAAe,SAAS,KAAK,MAAuC,GAAG;AACzF,kBAAY,mBAAmB,KAAK,MAAM,sBAAsB,eAAe,KAAK,IAAI,CAAC,EAAE;AAC3F,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI,KAAK,aAAa,UAAa,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,SAAS,KAAK,QAAQ,GAAG;AACxE,kBAAY,uBAAuB;AACnC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,OAAgC,CAAC;AACvC,QAAI,KAAK,UAAU,OAAW,MAAK,QAAQ,KAAK;AAChD,QAAI,KAAK,OAAQ,MAAK,SAAS,KAAK;AACpC,QAAI,KAAK,aAAa,OAAW,MAAK,WAAW,KAAK;AAEtD,QAAI,OAAO,KAAK,IAAI,EAAE,WAAW,GAAG;AAClC,kBAAY,gEAAgE;AAC5E,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,MAAM,MAAM,WAAW,MAAM;AACnC,UAAM,UAAU,MAAM,eAAe,QAAQ,IAAI,IAAI,UAAU;AAE/D,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO,MAAsB,aAAa,IAAI,EAAE,WAAW,OAAO,IAAI,IAAI;AAClG,UAAM,WAAW,MAAM,gBAAgB,QAAQ,IAAI,EAAE;AACrD,UAAM,WAAW,eAAe,OAAO,OAAO,SAAS,IAAI,MAAM,QAAQ;AAEzE;AAAA,MACE,EAAE,OAAO,SAAS;AAAA,MAClB,QAAQ,WAAW,iBAAiB,UAAU,QAAQ,CAAC,KAAK,SAAS,KAAK,EAAE;AAAA,IAC9E;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,aACG,QAAQ,qBAAqB,EAC7B,YAAY,oCAAoC,EAChD,OAAO,OAAO,eAAuB;AACpC,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,QAAQ,MAAM,aAAa,MAAM;AACvC,UAAM,UAAU,MAAM,eAAe,QAAQ,OAAO,UAAU;AAE9D,UAAM,OAAO,OAAO,aAAa,KAAK,WAAW,OAAO,EAAE;AAE1D;AAAA,MACE,EAAE,SAAS,MAAM,IAAI,QAAQ;AAAA,MAC7B,QAAQ,iBAAiB,UAAU,EAAE;AAAA,IACvC;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,aACG,QAAQ,qBAAqB,EAC7B,YAAY,oCAAoC,EAChD,eAAe,eAAe,sCAAsC,EACpE,OAAO,OAAO,YAAoB,SAAyB;AAC1D,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,MAAM,MAAM,WAAW,MAAM;AACnC,UAAM,UAAU,MAAM,eAAe,QAAQ,IAAI,IAAI,UAAU;AAE/D,QAAI,aAAa,KAAK;AACtB,QAAI,eAAe,QAAQ;AACzB,YAAM,KAAK,MAAM,OAAO,IAAoC,cAAc;AAE1E,YAAM,eAAe,GAAG,MAAM;AAC9B,UAAI,CAAC,cAAc;AACjB,oBAAY,iCAAiC;AAC7C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAGA,YAAM,EAAE,QAAQ,IAAI,MAAM,OAAO;AAAA,QAC/B,aAAa,IAAI,EAAE;AAAA,MACrB;AACA,YAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,gBAAgB,EAAE,YAAY,YAAY;AACtF,UAAI,CAAC,QAAQ;AACX,oBAAY,4CAA4C;AACxD,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,mBAAa,OAAO;AAAA,IACtB;AAEA,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO;AAAA,MAC7B,aAAa,IAAI,EAAE,WAAW,OAAO;AAAA,MACrC,EAAE,aAAa,WAAW;AAAA,IAC5B;AACA,UAAM,WAAW,MAAM,gBAAgB,QAAQ,IAAI,EAAE;AACrD,UAAM,WAAW,eAAe,OAAO,OAAO,SAAS,IAAI,MAAM,QAAQ;AAEzE;AAAA,MACE,EAAE,OAAO,SAAS;AAAA,MAClB,QAAQ,YAAY,iBAAiB,UAAU,QAAQ,CAAC,OAAO,KAAK,EAAE,EAAE;AAAA,IAC1E;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,aACG,QAAQ,uBAAuB,EAC/B,YAAY,+BAA+B,EAC3C,OAAO,OAAO,eAAuB;AACpC,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,MAAM,MAAM,WAAW,MAAM;AACnC,UAAM,UAAU,MAAM,eAAe,QAAQ,IAAI,IAAI,UAAU;AAE/D,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO;AAAA,MAC7B,aAAa,IAAI,EAAE,WAAW,OAAO;AAAA,MACrC,EAAE,aAAa,KAAK;AAAA,IACtB;AACA,UAAM,WAAW,MAAM,gBAAgB,QAAQ,IAAI,EAAE;AACrD,UAAM,WAAW,eAAe,OAAO,OAAO,SAAS,IAAI,MAAM,QAAQ;AAEzE;AAAA,MACE,EAAE,OAAO,SAAS;AAAA,MAClB,QAAQ,cAAc,iBAAiB,UAAU,QAAQ,CAAC,EAAE;AAAA,IAC9D;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;;;AGxeH,IAAAC,oBAAwB;AAsCxB,IAAMC,QAAO;AACb,IAAMC,OAAM;AACZ,IAAMC,SAAQ;AACd,IAAM,OAAO;AAEb,SAAS,aAAa,SAA0B;AAC9C,QAAM,OAAO,QAAQ,QAAQ,gBAAgB,QAAQ;AACrD,QAAM,SAAS,QAAQ,QAAQ,QAAQ,QAAQ,iBAAiB,UAAU,aAAa;AACvF,SAAO,GAAG,IAAI,GAAG,KAAK;AACxB;AAEA,SAAS,gBAAgB,IAAqB;AAC5C,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,IAAI,IAAI,KAAK,EAAE;AACrB,SAAO,EAAE,eAAe;AAC1B;AAMO,IAAM,iBAAiB,IAAI,0BAAQ,SAAS,EAChD,YAAY,uBAAuB;AAGtC,eACG,QAAQ,mBAAmB,EAC3B,YAAY,iEAAiE,EAC7E,OAAO,OAAO,eAAuB;AACpC,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,QAAQ,MAAM,aAAa,MAAM;AACvC,UAAM,UAAU,MAAM,eAAe,QAAQ,OAAO,UAAU;AAE9D,UAAM,OAAO,MAAM,OAAO;AAAA,MACxB,aAAa,KAAK,WAAW,OAAO;AAAA,IACtC;AAGA,QAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ;AACjE,iBAAW,WAAW,KAAK,UAAU;AACnC,gBAAQ,OAAO,MAAM,KAAK,UAAU,OAAO,IAAI,IAAI;AAAA,MACrD;AACA;AAAA,IACF;AAGA,QAAI,KAAK,SAAS,WAAW,GAAG;AAC9B,cAAQ,IAAI,oBAAoB;AAChC;AAAA,IACF;AAEA,eAAW,WAAW,KAAK,UAAU;AACnC,YAAM,SAAS,aAAa,OAAO;AACnC,YAAM,OAAO,gBAAgB,QAAQ,UAAU;AAC/C,cAAQ,IAAI,GAAGF,KAAI,GAAG,IAAI,GAAG,MAAM,GAAGE,MAAK,IAAID,IAAG,GAAG,IAAI,GAAGC,MAAK,EAAE;AACnE,cAAQ,IAAI,QAAQ,IAAI;AACxB,cAAQ,IAAI;AAAA,IACd;AACA,YAAQ,IAAI,GAAGD,IAAG,GAAG,KAAK,SAAS,MAAM,cAAcC,MAAK,EAAE;AAAA,EAChE,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,eACG,QAAQ,kBAAkB,EAC1B,YAAY,2BAA2B,EACvC,eAAe,iBAAiB,cAAc,EAC9C,OAAO,OAAO,YAAoB,SAA2B;AAC5D,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,QAAQ,MAAM,aAAa,MAAM;AACvC,UAAM,UAAU,MAAM,eAAe,QAAQ,OAAO,UAAU;AAE9D,UAAM,EAAE,QAAQ,IAAI,MAAM,OAAO;AAAA,MAC/B,aAAa,KAAK,WAAW,OAAO;AAAA,MACpC,EAAE,MAAM,KAAK,KAAK;AAAA,IACpB;AAEA;AAAA,MACE,EAAE,QAAQ;AAAA,MACV,2BAAsB,UAAU;AAAA,IAClC;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,eACG,QAAQ,qBAAqB,EAC7B,YAAY,kBAAkB,EAC9B,eAAe,iBAAiB,kBAAkB,EAClD,eAAe,wBAAwB,+CAA+C,EACtF,OAAO,OAAO,WAAmB,SAA0C;AAC1E,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,QAAQ,MAAM,aAAa,MAAM;AACvC,UAAM,UAAU,MAAM,eAAe,QAAQ,OAAO,KAAK,KAAK;AAE9D,UAAM,EAAE,QAAQ,IAAI,MAAM,OAAO;AAAA,MAC/B,aAAa,KAAK,WAAW,OAAO,aAAa,SAAS;AAAA,MAC1D,EAAE,MAAM,KAAK,KAAK;AAAA,IACpB;AAEA;AAAA,MACE,EAAE,QAAQ;AAAA,MACV,kBAAa,SAAS;AAAA,IACxB;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,eACG,QAAQ,qBAAqB,EAC7B,YAAY,kBAAkB,EAC9B,eAAe,wBAAwB,+CAA+C,EACtF,OAAO,OAAO,WAAmB,SAA4B;AAC5D,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,QAAQ,MAAM,aAAa,MAAM;AACvC,UAAM,UAAU,MAAM,eAAe,QAAQ,OAAO,KAAK,KAAK;AAE9D,UAAM,OAAO,OAAO,aAAa,KAAK,WAAW,OAAO,aAAa,SAAS,EAAE;AAEhF;AAAA,MACE,EAAE,SAAS,MAAM,IAAI,UAAU;AAAA,MAC/B,kBAAa,SAAS;AAAA,IACxB;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;;;AC9KH,IAAAC,oBAAwB;AAqBxB,SAAS,iBAAoC,SAAY,SAAiB,SAA6C;AACrH,SAAO,EAAE,GAAG,SAAS,KAAK,gBAAgB,SAAS,SAAS,QAAQ,IAAI,EAAE;AAC5E;AAUA,SAAS,YAAY,UAAkB,QAAQ,IAAY;AACzD,QAAM,SAAS,KAAK,MAAO,WAAW,MAAO,KAAK;AAClD,QAAM,QAAQ,QAAQ;AACtB,SAAO,IAAI,SAAI,OAAO,MAAM,CAAC,GAAG,SAAI,OAAO,KAAK,CAAC,KAAK,QAAQ;AAChE;AAEO,IAAM,iBAAiB,IAAI,0BAAQ,SAAS,EAChD,YAAY,iBAAiB,EAC7B,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA,oCAIY;AAIpC,eACG,QAAQ,MAAM,EACd,YAAY,iCAAiC,EAC7C,OAAO,YAAY;AAClB,QAAM,SAAS,IAAI,YAAY;AAC/B,MAAI;AACF,UAAM,MAAM,MAAM,WAAW,MAAM;AACnC,UAAM,OAAO,MAAM,OAAO,IAA6B,aAAa,IAAI,EAAE,WAAW;AACrF,UAAM,YAAY,KAAK,YAAY,CAAC,GAAG,IAAI,CAAC,MAAM,iBAAiB,GAAG,OAAO,SAAS,IAAI,IAAI,CAAC;AAE/F,QAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ;AACjE,iBAAW,KAAK,UAAU;AACxB,gBAAQ,OAAO,MAAM,KAAK,UAAU,CAAC,IAAI,IAAI;AAAA,MAC/C;AACA;AAAA,IACF;AAEA,QAAI,SAAS,WAAW,GAAG;AACzB,cAAQ,IAAI,IAAI,oBAAoB,CAAC;AACrC;AAAA,IACF;AAEA,eAAW,KAAK,UAAU;AACxB,YAAM,WAAW,EAAE,YAAY;AAC/B,YAAM,MAAM,YAAY,QAAQ;AAChC,cAAQ,IAAI,GAAG,KAAK,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,GAAG,CAAC,EAAE;AAChE,UAAI,EAAE,YAAa,SAAQ,IAAI,KAAK,IAAI,EAAE,WAAW,CAAC,EAAE;AACxD,cAAQ,IAAI,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,YAAY,EAAE,kBAAkB,CAAC,OAAO;AACxF,UAAI,EAAE,IAAK,SAAQ,IAAI,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE;AACxC,UAAI,EAAE,WAAW,WAAY,SAAQ,IAAI,KAAK,IAAI,YAAY,CAAC,EAAE;AACjE,cAAQ,IAAI,EAAE;AAAA,IAChB;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAIH,eACG,QAAQ,QAAQ,EAChB,YAAY,sBAAsB,EAClC,eAAe,iBAAiB,cAAc,EAC9C,OAAO,wBAAwB,qBAAqB,EACpD,OAAO,mBAAmB,uBAAuB,SAAS,EAC1D,OAAO,iBAAiB,wBAAwB,WAAI,EACpD,OAAO,OAAO,SAAS;AACtB,QAAM,SAAS,IAAI,YAAY;AAC/B,MAAI;AACF,UAAM,MAAM,MAAM,WAAW,MAAM;AACnC,UAAM,OAAO,MAAM,OAAO,KAA2B,aAAa,IAAI,EAAE,aAAa;AAAA,MACnF,MAAM,KAAK;AAAA,MACX,aAAa,KAAK,eAAe;AAAA,MACjC,OAAO,KAAK;AAAA,MACZ,MAAM,KAAK;AAAA,IACb,CAAC;AACD,UAAM,UAAU,iBAAiB,KAAK,SAAS,OAAO,SAAS,IAAI,IAAI;AAEvE;AAAA,MACE,EAAE,QAAQ;AAAA,MACV,QAAQ,mBAAmB,QAAQ,IAAI,IAAI,QAAQ,IAAI,KAAK,IAAI,QAAQ,EAAE,CAAC,IAAI,QAAQ,MAAM;AAAA,EAAK,QAAQ,GAAG,KAAK,EAAE,EAAE;AAAA,IACxH;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAIH,eACG,QAAQ,kBAAkB,EAC1B,YAAY,iCAAiC,EAC7C,OAAO,OAAO,cAAc;AAC3B,QAAM,SAAS,IAAI,YAAY;AAC/B,MAAI;AACF,UAAM,MAAM,MAAM,WAAW,MAAM;AACnC,UAAM,OAAO,MAAM,OAAO;AAAA,MACxB,aAAa,IAAI,EAAE,aAAa,SAAS;AAAA,IAC3C;AACA,UAAM,IAAI,iBAAiB,KAAK,SAAS,OAAO,SAAS,IAAI,IAAI;AAEjE,QAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ;AACjE,cAAQ,OAAO,MAAM,KAAK,UAAU,CAAC,IAAI,IAAI;AAC7C;AAAA,IACF;AAEA,UAAM,WAAW,EAAE,YAAY;AAC/B,YAAQ,IAAI,KAAK,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC;AACvC,QAAI,EAAE,YAAa,SAAQ,IAAI,KAAK,EAAE,WAAW,EAAE;AACnD,QAAI,EAAE,IAAK,SAAQ,IAAI,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE;AACxC,YAAQ,IAAI,KAAK,KAAK,YAAY,QAAQ,CAAC,CAAC,EAAE;AAC9C,YAAQ,IAAI,KAAM,EAAE,cAAc,CAAE,gBAAc,EAAE,kBAAkB,CAAE,YAAY;AACpF,YAAQ,IAAI,EAAE;AAEd,UAAM,SAAmB,EAAsC,UAAU,CAAC;AAC1E,QAAI,OAAO,WAAW,GAAG;AACvB,cAAQ,IAAI,IAAI,4BAA4B,CAAC;AAC7C;AAAA,IACF;AAEA,YAAQ,IAAI,KAAK,QAAQ,CAAC;AAC1B,eAAW,SAAS,QAAQ;AAC1B,YAAM,MAAM,MAAM,SAAS,IAAI,MAAM,MAAM,KAAK,MAAM,GAAG,MAAM,GAAG,CAAC;AACnE,cAAQ,IAAI,KAAK,IAAI,GAAG,CAAC,KAAK,MAAM,KAAK,KAAK,IAAI,IAAI,MAAM,MAAM,GAAG,CAAC,EAAE;AAAA,IAC1E;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;;;AC7JH,IAAAC,oBAAwB;AAkBxB,SAASC,aAAY,UAAkB,QAAQ,IAAY;AACzD,QAAM,SAAS,KAAK,MAAO,WAAW,MAAO,KAAK;AAClD,QAAM,QAAQ,QAAQ;AACtB,SAAO,IAAI,SAAI,OAAO,MAAM,CAAC,GAAG,SAAI,OAAO,KAAK,CAAC,KAAK,QAAQ;AAChE;AAEO,IAAM,mBAAmB,IAAI,0BAAQ,WAAW,EACpD,YAAY,mBAAmB,EAC/B,YAAY,SAAS;AAAA;AAAA;AAAA,kFAG0D;AAIlF,iBACG,QAAQ,MAAM,EACd,YAAY,+BAA+B,EAC3C,eAAe,kBAAkB,YAAY,EAC7C,OAAO,OAAO,SAAS;AACtB,QAAM,SAAS,IAAI,YAAY;AAC/B,MAAI;AACF,UAAM,QAAQ,MAAM,aAAa,MAAM;AACvC,UAAM,OAAO,MAAM,OAAO;AAAA,MACxB,aAAa,KAAK,aAAa,KAAK,OAAO;AAAA,IAC7C;AACA,UAAM,aAAa,KAAK,cAAc,CAAC;AAEvC,QAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ;AACjE,iBAAW,KAAK,YAAY;AAC1B,gBAAQ,OAAO,MAAM,KAAK,UAAU,CAAC,IAAI,IAAI;AAAA,MAC/C;AACA;AAAA,IACF;AAEA,QAAI,WAAW,WAAW,GAAG;AAC3B,cAAQ,IAAI,IAAI,sBAAsB,CAAC;AACvC;AAAA,IACF;AAEA,eAAW,KAAK,YAAY;AAC1B,YAAM,WAAW,EAAE,YAAY;AAC/B,YAAM,MAAMA,aAAY,QAAQ;AAChC,YAAM,UAAU,EAAE,YAAY,IAAI,IAAI,WAAW,CAAC,KAAK;AACvD,cAAQ,IAAI,GAAG,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,GAAG,CAAC,GAAG,OAAO,EAAE;AAC3D,UAAI,EAAE,SAAU,SAAQ,IAAI,UAAU,EAAE,QAAQ,EAAE;AAClD,UAAI,EAAE,YAAa,SAAQ,IAAI,KAAK,IAAI,EAAE,WAAW,CAAC,EAAE;AACxD,cAAQ,IAAI,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,YAAY,EAAE,kBAAkB,CAAC,OAAO;AACxF,UAAI,EAAE,WAAW,SAAU,SAAQ,IAAI,KAAK,IAAI,UAAU,CAAC,EAAE;AAC7D,cAAQ,IAAI,EAAE;AAAA,IAChB;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAIH,iBACG,QAAQ,QAAQ,EAChB,YAAY,wBAAwB,EACpC,eAAe,kBAAkB,YAAY,EAC7C,eAAe,iBAAiB,gBAAgB,EAChD,OAAO,uBAAuB,UAAU,EACxC,OAAO,wBAAwB,aAAa,EAC5C,OAAO,OAAO,SAAS;AACtB,QAAM,SAAS,IAAI,YAAY;AAC/B,MAAI;AACF,UAAM,QAAQ,MAAM,aAAa,MAAM;AACvC,UAAM,OAAO,MAAM,OAAO;AAAA,MACxB,aAAa,KAAK,aAAa,KAAK,OAAO;AAAA,MAC3C;AAAA,QACE,MAAM,KAAK;AAAA,QACX,aAAa,KAAK,eAAe;AAAA,QACjC,SAAS,KAAK,QAAQ;AAAA,MACxB;AAAA,IACF;AAEA;AAAA,MACE,EAAE,WAAW,KAAK,UAAU;AAAA,MAC5B,QAAQ,sBAAsB,KAAK,KAAK,UAAU,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,UAAU,EAAE,GAAG,CAAC,EAAE;AAAA,IAC5F;AACA,QAAI,KAAK,UAAU,UAAU;AAC3B,cAAQ,IAAI,UAAU,KAAK,UAAU,QAAQ,EAAE;AAAA,IACjD;AAAA,EACF,SAAS,KAAK;AACZ,mBAAe,GAAG;AAAA,EACpB;AACF,CAAC;;;AC1GH,IAAAC,oBAAwB;AACxB;AAIO,IAAM,gBAAgB,IAAI,0BAAQ,QAAQ,EAC9C,YAAY,+CAA+C,EAC3D,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,kDAK0B;AAIlD,cACG,QAAQ,MAAM,EACd,YAAY,+BAA+B,EAC3C,OAAO,oBAAoB,sBAAsB,EACjD,OAAO,CAAC,SAA+B;AACtC,QAAM,MAAM,WAAW;AACvB,QAAM,gBAAgB,IAAI;AAC1B,QAAM,WAAW,cAAc,EAAE,SAAS,KAAK,QAAQ,CAAC;AACxD,QAAM,YAAY,CAAC,CAAC,SAAS;AAE7B,MAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ;AACjE;AAAA,MACE;AAAA,QACE,eAAe,iBAAiB;AAAA,QAChC,iBAAiB,SAAS,WAAW;AAAA,QACrC,SAAS,SAAS,WAAW;AAAA,QAC7B,aAAa,SAAS,eAAe;AAAA,QACrC,SAAS,SAAS,WAAW;AAAA,QAC7B;AAAA,QACA,UAAU,OAAO,KAAK,IAAI,YAAY,CAAC,CAAC,EAAE,KAAK;AAAA,MACjD;AAAA,MACA;AAAA,IACF;AACA;AAAA,EACF;AAEA,UAAQ,IAAI,GAAG,KAAK,yBAAyB,CAAC,EAAE;AAChD,UAAQ,IAAI,KAAK,IAAI,iBAAiB,CAAC,IAAI,gBAAgB,KAAK,aAAa,IAAI,KAAK,WAAW,CAAC,EAAE;AACpG,UAAQ,IAAI,KAAK,IAAI,mBAAmB,CAAC,IAAI,SAAS,UAAU,KAAK,SAAS,OAAO,IAAI,KAAK,iBAAiB,CAAC,EAAE;AAClH,UAAQ,IAAI,KAAK,IAAI,WAAW,CAAC,OAAO,SAAS,UAAU,KAAK,SAAS,OAAO,IAAI,KAAK,WAAW,CAAC,EAAE;AACvG,UAAQ,IAAI,KAAK,IAAI,eAAe,CAAC,IAAI,SAAS,cAAc,KAAK,SAAS,WAAW,IAAI,KAAK,WAAW,CAAC,EAAE;AAChH,UAAQ,IAAI,KAAK,IAAI,WAAW,CAAC,OAAO,SAAS,UAAU,KAAK,SAAS,OAAO,IAAI,KAAK,WAAW,CAAC,EAAE;AACvG,UAAQ,IAAI,KAAK,IAAI,UAAU,CAAC,QAAQ,YAAY,KAAK,KAAK,IAAI,KAAK,uCAAkC,CAAC,EAAE;AAC9G,CAAC;AAIH,cACG,QAAQ,gBAAgB,EACxB,YAAY,mCAAmC,EAC/C,OAAO,oBAAoB,wBAAwB,EACnD,OAAO,CAAC,MAAc,SAA+B;AACpD,QAAM,MAAM,WAAW;AACvB,QAAM,cAAc,KAAK,WAAW,QAAQ,IAAI,iBAAiB,IAAI;AACrE,MAAI,aAAa;AACf,kBAAc,KAAK,WAAW,EAAE,UAAU;AAAA,EAC5C,OAAO;AACL,QAAI,UAAU;AAAA,EAChB;AACA,cAAY,GAAG;AAEf;AAAA,IACE,EAAE,SAAS,MAAM,SAAS,eAAe,KAAK;AAAA,IAC9C,cACI,QAAQ,4BAA4B,WAAW,aAAa,IAAI,GAAG,IACnE,QAAQ,uBAAuB,IAAI,GAAG;AAAA,EAC5C;AACF,CAAC;AAIH,cACG,QAAQ,iBAAiB,EACzB,YAAY,iCAAiC,EAC7C,OAAO,oBAAoB,wBAAwB,EACnD,OAAO,CAAC,MAAc,SAA+B;AACpD,QAAM,MAAM,WAAW;AACvB,QAAM,cAAc,KAAK,WAAW,QAAQ,IAAI,iBAAiB,IAAI;AACrE,MAAI,aAAa;AACf,kBAAc,KAAK,WAAW,EAAE,cAAc;AAAA,EAChD,OAAO;AACL,QAAI,cAAc;AAAA,EACpB;AACA,cAAY,GAAG;AAEf;AAAA,IACE,EAAE,aAAa,MAAM,SAAS,eAAe,KAAK;AAAA,IAClD,cACI,QAAQ,6BAA6B,WAAW,aAAa,IAAI,GAAG,IACpE,QAAQ,wBAAwB,IAAI,GAAG;AAAA,EAC7C;AACF,CAAC;AAIH,cACG,QAAQ,oBAAoB,EAC5B,YAAY,8BAA8B,EAC1C,OAAO,oBAAoB,wBAAwB,EACnD,OAAO,CAAC,KAAa,SAA+B;AACnD,QAAM,MAAM,WAAW;AACvB,QAAM,cAAc,KAAK,WAAW,QAAQ,IAAI,iBAAiB,IAAI;AACrE,MAAI,aAAa;AACf,kBAAc,KAAK,WAAW,EAAE,UAAU;AAAA,EAC5C,OAAO;AACL,QAAI,UAAU;AAAA,EAChB;AACA,cAAY,GAAG;AAEf;AAAA,IACE,EAAE,SAAS,KAAK,SAAS,eAAe,KAAK;AAAA,IAC7C,cACI,QAAQ,yBAAyB,WAAW,aAAa,GAAG,GAAG,IAC/D,QAAQ,oBAAoB,GAAG,GAAG;AAAA,EACxC;AACF,CAAC;;;ACzHH,IAAAC,oBAAwB;AA8BxB,SAASC,gBAAe,KAAqB;AAC3C,QAAM,MAAO,IAAc,WAAW,OAAO,GAAG;AAChD,MAAI,YAAY,KAAK,GAAG,GAAG;AACzB,gBAAY,mEAA8D;AAAA,EAC5E,WAAW,YAAY,KAAK,GAAG,GAAG;AAChC,gBAAY,6DAA6D;AAAA,EAC3E,OAAO;AACL,gBAAY,GAAG;AAAA,EACjB;AACA,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAeC,cAAa,QAAqB,iBAA2C;AAC1F,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,IAA8C,WAAW;AACvF,MAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,gBAAY,yBAAyB;AACrC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,KAAK,WAAW,EAAG,QAAO,KAAK,CAAC,EAAE;AAEtC,QAAM,EAAE,eAAAC,eAAc,IAAI,MAAM;AAChC,QAAM,SAASA,eAAc;AAC7B,QAAM,OAAO,mBAAmB,OAAO;AAEvC,MAAI,MAAM;AACR,UAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAC9C,QAAI,MAAO,QAAO,MAAM;AACxB,gBAAY,QAAQ,IAAI,2BAA2B,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AACvF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,cAAY,wCAAwC;AACpD,UAAQ,KAAK,CAAC;AAChB;AAEA,SAASC,QAAO,KAAa,KAAqB;AAChD,SAAO,IAAI,UAAU,MAAM,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,OAAO,MAAM,IAAI,MAAM;AAClF;AAMO,IAAM,iBAAiB,IAAI,0BAAQ,SAAS,EAChD,YAAY,0BAA0B,EACtC,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA,8BAIM;AAG9B,eACG,QAAQ,MAAM,EACd,YAAY,mCAAmC,EAC/C,OAAO,YAAY;AAClB,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,QAAQ,MAAMF,cAAa,MAAM;AAEvC,UAAM,OAAO,MAAM,OAAO,IAAyB,uBAAuB,KAAK,EAAE;AAEjF,QAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,IAAI,kBAAkB,QAAQ;AACjE,iBAAW,MAAM,KAAK,UAAU;AAC9B,gBAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,IAAI,IAAI;AAAA,MAChD;AACA;AAAA,IACF;AAEA,QAAI,KAAK,SAAS,WAAW,GAAG;AAC9B,cAAQ,IAAI,yBAAyB;AACrC;AAAA,IACF;AAEA,UAAM,SAAS,KAAK,GAAGE,QAAO,MAAM,EAAE,CAAC,IAAIA,QAAO,OAAO,EAAE,CAAC,IAAIA,QAAO,UAAU,EAAE,CAAC,SAAS;AAC7F,YAAQ,IAAI,MAAM;AAClB,YAAQ,IAAI,SAAI,OAAO,GAAG,CAAC;AAC3B,eAAW,MAAM,KAAK,UAAU;AAC9B,YAAM,OAAO,GAAG,OAAO,WAAW,IAAI,QAAQ,GAAG,OAAO,KAAK,GAAG;AAChE,YAAM,SAAS,GAAG,UAAU,MAAM,KAAK,IAAI,KAAK,IAAI;AACpD,YAAM,MAAM,GAAG,IAAI,SAAS,KAAK,GAAG,IAAI,MAAM,GAAG,EAAE,IAAI,WAAM,GAAG;AAChE,YAAM,SAAS,KAAK,SAAS,KAAK,KAAK,MAAM,GAAG,EAAE,IAAI,WAAM;AAC5D,cAAQ,IAAI,GAAGA,QAAO,GAAG,GAAG,MAAM,GAAG,EAAE,GAAG,EAAE,CAAC,IAAIA,QAAO,KAAK,EAAE,CAAC,IAAIA,QAAO,QAAQ,EAAE,CAAC,IAAI,MAAM,EAAE;AAAA,IACpG;AACA,YAAQ,IAAI,IAAI,GAAG,KAAK,SAAS,MAAM,aAAa,CAAC;AAAA,EACvD,SAAS,KAAK;AACZ,IAAAH,gBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,eACG,QAAQ,QAAQ,EAChB,YAAY,sBAAsB,EAClC,eAAe,eAAe,qBAAqB,EACnD,OAAO,qBAAqB,8CAA8C,EAAE,EAC5E,OAAO,OAAO,SAA0C;AACvD,MAAI;AACF,QAAI,CAAC,KAAK,IAAI,WAAW,UAAU,GAAG;AACpC,kBAAY,qBAAqB;AACjC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,KAAK,SAChB,KAAK,OAAO,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,IAC1D,CAAC;AAEL,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,QAAQ,MAAMC,cAAa,MAAM;AAEvC,UAAM,OAAO,MAAM,OAAO;AAAA,MACxB,uBAAuB,KAAK;AAAA,MAC5B,EAAE,KAAK,KAAK,KAAK,OAAO;AAAA,IAC1B;AAEA;AAAA,MACE,EAAE,SAAS,KAAK,SAAS,QAAQ,KAAK,OAAO;AAAA,MAC7C;AAAA,QACE,QAAQ,mBAAmB,KAAK,QAAQ,EAAE,EAAE;AAAA,QAC5C;AAAA,QACA,KAAK,oDAA+C;AAAA,QACpD,KAAK,KAAK,MAAM;AAAA,MAClB,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF,SAAS,KAAK;AACZ,IAAAD,gBAAe,GAAG;AAAA,EACpB;AACF,CAAC;AAGH,eACG,QAAQ,aAAa,EACrB,YAAY,kBAAkB,EAC9B,OAAO,OAAO,OAAe;AAC5B,MAAI;AACF,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,OAAO,OAAO,iBAAiB,EAAE,EAAE;AAEzC;AAAA,MACE,EAAE,SAAS,MAAM,GAAG;AAAA,MACpB,QAAQ,mBAAmB,EAAE,EAAE;AAAA,IACjC;AAAA,EACF,SAAS,KAAK;AACZ,IAAAA,gBAAe,GAAG;AAAA,EACpB;AACF,CAAC;;;AXrKH,IAAM,UAAU,IAAI,0BAAQ,OAAO,EAChC,YAAY,uDAAkD,EAC9D,QAAQ,OAAO,EACf,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAOF,EAEnB,OAAO,oBAAoB,+CAA+C,EAC1E,OAAO,gBAAgB,4CAA4C,EACnE,OAAO,eAAe,iDAAiD,EACvE,KAAK,aAAa,CAAC,gBAAgB;AAClC,QAAM,OAAO,YAAY,KAAK;AAC9B,MAAI,KAAK,SAAS;AAChB,YAAQ,IAAI,gBAAgB,KAAK;AAAA,EACnC;AACA,MAAI,KAAK,KAAK;AACZ,YAAQ,IAAI,YAAY,KAAK;AAC7B,YAAQ,IAAI,gBAAgB,KAAK;AAAA,EACnC;AACA,MAAI,KAAK,MAAM;AACb,YAAQ,IAAI,aAAa,KAAK;AAC9B,YAAQ,IAAI,iBAAiB,KAAK;AAAA,EACpC;AACF,CAAC;AAEH,QAAQ,WAAW,WAAW;AAC9B,QAAQ,WAAW,YAAY;AAC/B,QAAQ,WAAW,cAAc;AACjC,QAAQ,WAAW,cAAc;AACjC,QAAQ,WAAW,gBAAgB;AACnC,QAAQ,WAAW,aAAa;AAChC,QAAQ,WAAW,cAAc;AAIjC,IAAM,oBAAoB,IAAI,0BAAQ,YAAY,EAC/C,YAAY,iCAAiC,EAC7C,YAAY,SAAS;AAAA;AAAA;AAAA,sCAGc;AAEtC,kBACG,QAAQ,MAAM,EACd,YAAY,+BAA+B,EAC3C,OAAO,MAAM;AACZ,UAAQ,OAAO,MAAM,eAAe,CAAC;AACvC,CAAC;AAEH,kBACG,QAAQ,KAAK,EACb,YAAY,8BAA8B,EAC1C,OAAO,MAAM;AACZ,UAAQ,OAAO,MAAM,cAAc,CAAC;AACtC,CAAC;AAEH,QAAQ,WAAW,iBAAiB;AAEpC,QAAQ,MAAM;AAMd,SAAS,iBAAyB;AAChC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8DT;AAEA,SAAS,gBAAwB;AAC/B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6ET;","names":["import_commander","import_commander","resolveConfig","import_commander","BOLD","DIM","RESET","import_commander","import_commander","progressBar","import_commander","import_commander","handleApiError","resolveOrgId","resolveConfig","padEnd"]}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atollhq/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Atoll CLI — project management from the terminal",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"bin": {
|
|
8
|
-
"atoll": "
|
|
8
|
+
"atoll": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|