@hasna/todos 0.13.6 → 0.13.8
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/dist/cli/assignee-guard.d.ts +13 -0
- package/dist/cli/assignee-guard.d.ts.map +1 -0
- package/dist/cli/claim-guard.d.ts +3 -0
- package/dist/cli/claim-guard.d.ts.map +1 -0
- package/dist/cli/commands/query-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/index.js +3299 -3076
- package/dist/contracts.js +81 -29
- package/dist/db/task-crud.d.ts.map +1 -1
- package/dist/db/task-lifecycle.d.ts.map +1 -1
- package/dist/index.js +114 -58
- package/dist/lib/agent-tasks.d.ts.map +1 -1
- package/dist/lib/assignee-context.d.ts +40 -0
- package/dist/lib/assignee-context.d.ts.map +1 -0
- package/dist/lib/assignee-validation.d.ts +72 -0
- package/dist/lib/assignee-validation.d.ts.map +1 -0
- package/dist/lib/claude-tasks.d.ts.map +1 -1
- package/dist/lib/creator-identity.d.ts +30 -0
- package/dist/lib/creator-identity.d.ts.map +1 -1
- package/dist/lib/sync-utils.d.ts +7 -0
- package/dist/lib/sync-utils.d.ts.map +1 -1
- package/dist/mcp/index.js +245 -112
- package/dist/mcp/tools/task-crud.d.ts.map +1 -1
- package/dist/mcp.js +1 -1
- package/dist/registry.js +81 -29
- package/dist/release-provenance.json +5 -5
- package/dist/server/index.js +249 -116
- package/dist/storage.js +66 -14
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4336,6 +4336,7 @@ var init_database = __esm(() => {
|
|
|
4336
4336
|
|
|
4337
4337
|
// src/lib/sync-utils.ts
|
|
4338
4338
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
4339
|
+
import { createHash } from "crypto";
|
|
4339
4340
|
import { join as join2 } from "path";
|
|
4340
4341
|
function getHomeDir() {
|
|
4341
4342
|
return process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
@@ -4391,7 +4392,40 @@ function appendSyncConflict(metadata, conflict, limit = 5) {
|
|
|
4391
4392
|
const next = [conflict, ...current].slice(0, limit);
|
|
4392
4393
|
return { ...metadata, sync_conflicts: next };
|
|
4393
4394
|
}
|
|
4394
|
-
|
|
4395
|
+
function canonicalize(value) {
|
|
4396
|
+
if (Array.isArray(value))
|
|
4397
|
+
return value.map(canonicalize);
|
|
4398
|
+
if (!value || typeof value !== "object")
|
|
4399
|
+
return value;
|
|
4400
|
+
const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined).sort(([a], [b]) => a.localeCompare(b));
|
|
4401
|
+
return Object.fromEntries(entries.map(([key, entryValue]) => [key, canonicalize(entryValue)]));
|
|
4402
|
+
}
|
|
4403
|
+
function withoutSyncFingerprintMetadata(metadata) {
|
|
4404
|
+
const { [TODO_SYNC_FINGERPRINT_KEY]: _fingerprint, ...rest } = metadata;
|
|
4405
|
+
return rest;
|
|
4406
|
+
}
|
|
4407
|
+
function syncFingerprint(record) {
|
|
4408
|
+
const metadata = withoutSyncFingerprintMetadata(record.metadata || {});
|
|
4409
|
+
const canonical = canonicalize({ ...record, metadata });
|
|
4410
|
+
return `sha256:${createHash("sha256").update(JSON.stringify(canonical)).digest("hex")}`;
|
|
4411
|
+
}
|
|
4412
|
+
function withSyncFingerprint(record) {
|
|
4413
|
+
const metadata = withoutSyncFingerprintMetadata(record.metadata);
|
|
4414
|
+
return {
|
|
4415
|
+
...record,
|
|
4416
|
+
metadata: {
|
|
4417
|
+
...metadata,
|
|
4418
|
+
[TODO_SYNC_FINGERPRINT_KEY]: syncFingerprint({ ...record, metadata })
|
|
4419
|
+
}
|
|
4420
|
+
};
|
|
4421
|
+
}
|
|
4422
|
+
function hasSyncFingerprintChanged(record) {
|
|
4423
|
+
const stored = record.metadata?.[TODO_SYNC_FINGERPRINT_KEY];
|
|
4424
|
+
if (typeof stored !== "string" || stored.length === 0)
|
|
4425
|
+
return null;
|
|
4426
|
+
return stored !== syncFingerprint(record);
|
|
4427
|
+
}
|
|
4428
|
+
var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
|
|
4395
4429
|
var init_sync_utils = __esm(() => {
|
|
4396
4430
|
HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
4397
4431
|
});
|
|
@@ -5502,7 +5536,7 @@ var init_runner_sandbox = __esm(() => {
|
|
|
5502
5536
|
});
|
|
5503
5537
|
|
|
5504
5538
|
// src/lib/event-hooks.ts
|
|
5505
|
-
import { createHash, randomUUID } from "crypto";
|
|
5539
|
+
import { createHash as createHash2, randomUUID } from "crypto";
|
|
5506
5540
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
5507
5541
|
import { dirname as dirname3, resolve as resolve6 } from "path";
|
|
5508
5542
|
import { createConnection } from "net";
|
|
@@ -5569,7 +5603,7 @@ function buildEnvelope(type, payload, timestamp2 = new Date().toISOString()) {
|
|
|
5569
5603
|
payload: redactValue(payload ?? {}),
|
|
5570
5604
|
source: { package: "@hasna/todos", local_only: true }
|
|
5571
5605
|
};
|
|
5572
|
-
const digest =
|
|
5606
|
+
const digest = createHash2("sha256").update(canonicalEvent(base)).digest("hex");
|
|
5573
5607
|
return { ...base, integrity: { algorithm: "sha256", digest } };
|
|
5574
5608
|
}
|
|
5575
5609
|
function summarize(value) {
|
|
@@ -7479,6 +7513,14 @@ var init_checklists = __esm(() => {
|
|
|
7479
7513
|
init_database();
|
|
7480
7514
|
});
|
|
7481
7515
|
|
|
7516
|
+
// src/lib/creator-identity.ts
|
|
7517
|
+
function canonicalAgentRef(value) {
|
|
7518
|
+
return value.trim().toLowerCase();
|
|
7519
|
+
}
|
|
7520
|
+
var init_creator_identity = __esm(() => {
|
|
7521
|
+
init_sync_utils();
|
|
7522
|
+
});
|
|
7523
|
+
|
|
7482
7524
|
// src/lib/recurrence.ts
|
|
7483
7525
|
function parseRecurrenceRule(rule) {
|
|
7484
7526
|
const normalized = rule.trim().toLowerCase();
|
|
@@ -8185,6 +8227,11 @@ __export(exports_task_lifecycle, {
|
|
|
8185
8227
|
claimOrSteal: () => claimOrSteal,
|
|
8186
8228
|
claimNextTask: () => claimNextTask
|
|
8187
8229
|
});
|
|
8230
|
+
function sameHolder(stored, incoming) {
|
|
8231
|
+
if (!stored || !incoming)
|
|
8232
|
+
return false;
|
|
8233
|
+
return canonicalAgentRef(stored) === canonicalAgentRef(incoming);
|
|
8234
|
+
}
|
|
8188
8235
|
function lockExpiresAt(lockedAt) {
|
|
8189
8236
|
if (!lockedAt)
|
|
8190
8237
|
return null;
|
|
@@ -8235,13 +8282,13 @@ function startTask(id, agentId, db) {
|
|
|
8235
8282
|
const cutoff = lockExpiryCutoff();
|
|
8236
8283
|
const timestamp2 = now();
|
|
8237
8284
|
const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, started_at = COALESCE(started_at, ?), version = version + 1, updated_at = ?
|
|
8238
|
-
WHERE id = ? AND status IN ('pending', 'in_progress') AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp2, timestamp2, timestamp2, id, agentId, cutoff]);
|
|
8285
|
+
WHERE id = ? AND status IN ('pending', 'in_progress') AND (locked_by IS NULL OR LOWER(TRIM(locked_by)) = LOWER(TRIM(?)) OR locked_at < ?)`, [agentId, agentId, timestamp2, timestamp2, timestamp2, id, agentId, cutoff]);
|
|
8239
8286
|
if (result.changes === 0) {
|
|
8240
8287
|
const current = getTask(id, d);
|
|
8241
8288
|
if (!current)
|
|
8242
8289
|
throw new TaskNotFoundError(id);
|
|
8243
8290
|
assertStartable(current, agentId);
|
|
8244
|
-
if (current.locked_by && current.locked_by
|
|
8291
|
+
if (current.locked_by && !sameHolder(current.locked_by, agentId) && !isLockExpired(current.locked_at)) {
|
|
8245
8292
|
throw new LockError(id, current.locked_by);
|
|
8246
8293
|
}
|
|
8247
8294
|
throw new Error(`Task ${id} could not be started because it changed during claim`);
|
|
@@ -8266,7 +8313,7 @@ function completeTask(id, agentId, db, options) {
|
|
|
8266
8313
|
if (task.status === "cancelled") {
|
|
8267
8314
|
throw new Error(`Task ${id} is cancelled and cannot be completed`);
|
|
8268
8315
|
}
|
|
8269
|
-
if (agentId && task.locked_by && task.locked_by
|
|
8316
|
+
if (agentId && task.locked_by && !sameHolder(task.locked_by, agentId) && !isLockExpired(task.locked_at)) {
|
|
8270
8317
|
throw new LockError(id, task.locked_by);
|
|
8271
8318
|
}
|
|
8272
8319
|
checkCompletionGuard(task, agentId || null, d);
|
|
@@ -8388,16 +8435,16 @@ function lockTask(id, agentId, db) {
|
|
|
8388
8435
|
error: `Task is ${task.status} and cannot be locked`
|
|
8389
8436
|
};
|
|
8390
8437
|
}
|
|
8391
|
-
if (task.locked_by
|
|
8438
|
+
if (sameHolder(task.locked_by, agentId) && !isLockExpired(task.locked_at)) {
|
|
8392
8439
|
const timestamp3 = now();
|
|
8393
|
-
d.run(`UPDATE tasks SET locked_at = ?, updated_at = ?, version = version + 1 WHERE id = ? AND locked_by =
|
|
8440
|
+
d.run(`UPDATE tasks SET locked_at = ?, updated_at = ?, version = version + 1 WHERE id = ? AND LOWER(TRIM(locked_by)) = LOWER(TRIM(?))`, [timestamp3, timestamp3, id, agentId]);
|
|
8394
8441
|
logTaskChange(id, "lock_renew", "locked_by", agentId, agentId, agentId, d);
|
|
8395
8442
|
return { success: true, locked_by: agentId, locked_at: timestamp3, expires_at: lockExpiresAt(timestamp3) };
|
|
8396
8443
|
}
|
|
8397
8444
|
const cutoff = lockExpiryCutoff();
|
|
8398
8445
|
const timestamp2 = now();
|
|
8399
8446
|
const result = d.run(`UPDATE tasks SET locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
|
|
8400
|
-
WHERE id = ? AND status NOT IN ('completed', 'cancelled') AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, timestamp2, timestamp2, id, agentId, cutoff]);
|
|
8447
|
+
WHERE id = ? AND status NOT IN ('completed', 'cancelled') AND (locked_by IS NULL OR LOWER(TRIM(locked_by)) = LOWER(TRIM(?)) OR locked_at < ?)`, [agentId, timestamp2, timestamp2, id, agentId, cutoff]);
|
|
8401
8448
|
if (result.changes === 0) {
|
|
8402
8449
|
const current = getTask(id, d);
|
|
8403
8450
|
if (!current)
|
|
@@ -8429,7 +8476,7 @@ function unlockTask(id, agentId, db) {
|
|
|
8429
8476
|
const task = getTask(id, d);
|
|
8430
8477
|
if (!task)
|
|
8431
8478
|
throw new TaskNotFoundError(id);
|
|
8432
|
-
if (agentId && task.locked_by && task.locked_by
|
|
8479
|
+
if (agentId && task.locked_by && !sameHolder(task.locked_by, agentId)) {
|
|
8433
8480
|
throw new LockError(id, task.locked_by);
|
|
8434
8481
|
}
|
|
8435
8482
|
const timestamp2 = now();
|
|
@@ -8722,6 +8769,7 @@ var MAX_SPAWN_DEPTH = 10;
|
|
|
8722
8769
|
var init_task_lifecycle = __esm(() => {
|
|
8723
8770
|
init_types();
|
|
8724
8771
|
init_database();
|
|
8772
|
+
init_creator_identity();
|
|
8725
8773
|
init_completion_guard();
|
|
8726
8774
|
init_event_emission_safety();
|
|
8727
8775
|
init_event_hooks();
|
|
@@ -9177,6 +9225,10 @@ function updateTask(id, input, db) {
|
|
|
9177
9225
|
sets.push("description = ?");
|
|
9178
9226
|
params.push(input.description);
|
|
9179
9227
|
}
|
|
9228
|
+
if (input.agent_id !== undefined) {
|
|
9229
|
+
sets.push("agent_id = ?");
|
|
9230
|
+
params.push(input.agent_id);
|
|
9231
|
+
}
|
|
9180
9232
|
if (input.status !== undefined) {
|
|
9181
9233
|
if (input.status === "completed") {
|
|
9182
9234
|
checkCompletionGuard(task, task.assigned_to || task.agent_id || null, d);
|
|
@@ -10486,7 +10538,7 @@ var init_boards = __esm(() => {
|
|
|
10486
10538
|
});
|
|
10487
10539
|
|
|
10488
10540
|
// src/lib/artifact-store.ts
|
|
10489
|
-
import { createHash as
|
|
10541
|
+
import { createHash as createHash3 } from "crypto";
|
|
10490
10542
|
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync3, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
10491
10543
|
import { basename, dirname as dirname4, join as join5, resolve as resolve7 } from "path";
|
|
10492
10544
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -10511,7 +10563,7 @@ function artifactStorePath(relativePath) {
|
|
|
10511
10563
|
return join5(artifactStoreRoot(), normalized);
|
|
10512
10564
|
}
|
|
10513
10565
|
function sha256(buffer) {
|
|
10514
|
-
return
|
|
10566
|
+
return createHash3("sha256").update(buffer).digest("hex");
|
|
10515
10567
|
}
|
|
10516
10568
|
function isTextLike(buffer, path) {
|
|
10517
10569
|
if (buffer.includes(0))
|
|
@@ -12034,7 +12086,7 @@ var init_dispatches = __esm(() => {
|
|
|
12034
12086
|
// package.json
|
|
12035
12087
|
var package_default = {
|
|
12036
12088
|
name: "@hasna/todos",
|
|
12037
|
-
version: "0.13.
|
|
12089
|
+
version: "0.13.8",
|
|
12038
12090
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
12039
12091
|
type: "module",
|
|
12040
12092
|
main: "dist/index.js",
|
|
@@ -15749,7 +15801,7 @@ function importOnboardingFixture(options = {}) {
|
|
|
15749
15801
|
}
|
|
15750
15802
|
// src/lib/local-backups.ts
|
|
15751
15803
|
init_database();
|
|
15752
|
-
import { createHash as
|
|
15804
|
+
import { createHash as createHash4 } from "crypto";
|
|
15753
15805
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
15754
15806
|
import { dirname as dirname5, resolve as resolve8 } from "path";
|
|
15755
15807
|
import { mkdirSync as mkdirSync6 } from "fs";
|
|
@@ -15767,7 +15819,7 @@ function stableJson(value) {
|
|
|
15767
15819
|
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
|
|
15768
15820
|
}
|
|
15769
15821
|
function sha2562(value) {
|
|
15770
|
-
return
|
|
15822
|
+
return createHash4("sha256").update(stableJson(value)).digest("hex");
|
|
15771
15823
|
}
|
|
15772
15824
|
function sqliteIntegrity(db) {
|
|
15773
15825
|
let quick = "unknown";
|
|
@@ -16045,7 +16097,7 @@ function checkLocalIntegrity(options = {}, db) {
|
|
|
16045
16097
|
}
|
|
16046
16098
|
// src/lib/local-snapshots.ts
|
|
16047
16099
|
init_database();
|
|
16048
|
-
import { createHash as
|
|
16100
|
+
import { createHash as createHash5 } from "crypto";
|
|
16049
16101
|
|
|
16050
16102
|
// src/lib/activity-timeline.ts
|
|
16051
16103
|
init_database();
|
|
@@ -16321,7 +16373,7 @@ function stable(value) {
|
|
|
16321
16373
|
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, stable(item)]));
|
|
16322
16374
|
}
|
|
16323
16375
|
function sha2563(value) {
|
|
16324
|
-
return
|
|
16376
|
+
return createHash5("sha256").update(JSON.stringify(stable(value))).digest("hex");
|
|
16325
16377
|
}
|
|
16326
16378
|
function latestTimestamp(items, fallback) {
|
|
16327
16379
|
const timestamps = [];
|
|
@@ -19533,20 +19585,20 @@ init_database();
|
|
|
19533
19585
|
init_task_runs();
|
|
19534
19586
|
init_config2();
|
|
19535
19587
|
init_redaction();
|
|
19536
|
-
import { createHash as
|
|
19588
|
+
import { createHash as createHash6 } from "crypto";
|
|
19537
19589
|
var LOCAL_AUDIT_LEDGER_SCHEMA_VERSION = 1;
|
|
19538
19590
|
var LOCAL_AUDIT_LEDGER_HASH_ALGORITHM = "sha256";
|
|
19539
19591
|
var LOCAL_AUDIT_LEDGER_INITIAL_HASH = "0".repeat(64);
|
|
19540
|
-
function
|
|
19592
|
+
function canonicalize2(value) {
|
|
19541
19593
|
if (value === null || typeof value !== "object")
|
|
19542
19594
|
return JSON.stringify(value);
|
|
19543
19595
|
if (Array.isArray(value))
|
|
19544
|
-
return `[${value.map(
|
|
19596
|
+
return `[${value.map(canonicalize2).join(",")}]`;
|
|
19545
19597
|
const object = value;
|
|
19546
|
-
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${
|
|
19598
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${canonicalize2(object[key])}`).join(",")}}`;
|
|
19547
19599
|
}
|
|
19548
19600
|
function hash(value) {
|
|
19549
|
-
return
|
|
19601
|
+
return createHash6("sha256").update(value).digest("hex");
|
|
19550
19602
|
}
|
|
19551
19603
|
function parsePayload2(value) {
|
|
19552
19604
|
if (!value)
|
|
@@ -19679,7 +19731,7 @@ function toLedgerEntries(rows) {
|
|
|
19679
19731
|
let previous = LOCAL_AUDIT_LEDGER_INITIAL_HASH;
|
|
19680
19732
|
return ordered.map((row, index) => {
|
|
19681
19733
|
const payload = parsePayload2(row.payload_json);
|
|
19682
|
-
const payloadHash = hash(
|
|
19734
|
+
const payloadHash = hash(canonicalize2({ source: row.source, source_id: row.source_id, created_at: row.created_at, payload }));
|
|
19683
19735
|
const chainHash = hash(`${previous}
|
|
19684
19736
|
${payloadHash}`);
|
|
19685
19737
|
const entry = {
|
|
@@ -20066,7 +20118,7 @@ function renderReleaseCompatibilityMarkdown(report) {
|
|
|
20066
20118
|
`);
|
|
20067
20119
|
}
|
|
20068
20120
|
// src/db/inbox.ts
|
|
20069
|
-
import { createHash as
|
|
20121
|
+
import { createHash as createHash7 } from "crypto";
|
|
20070
20122
|
|
|
20071
20123
|
// src/lib/github.ts
|
|
20072
20124
|
import { execFileSync } from "child_process";
|
|
@@ -20151,7 +20203,7 @@ function compactWhitespace(value) {
|
|
|
20151
20203
|
function fingerprintInboxInput(input) {
|
|
20152
20204
|
const sourceType = input.source_type || detectInboxSourceType(input.body, input.source_url);
|
|
20153
20205
|
const normalized = compactWhitespace(sanitizePreWriteText(input.body, "inbox.fingerprint")).slice(0, 8000);
|
|
20154
|
-
return
|
|
20206
|
+
return createHash7("sha256").update(`${sourceType}
|
|
20155
20207
|
${input.source_url || ""}
|
|
20156
20208
|
${normalized}`).digest("hex");
|
|
20157
20209
|
}
|
|
@@ -21268,7 +21320,7 @@ function importExternalIssues(input, db) {
|
|
|
21268
21320
|
init_database();
|
|
21269
21321
|
init_tasks();
|
|
21270
21322
|
init_redaction();
|
|
21271
|
-
import { createHash as
|
|
21323
|
+
import { createHash as createHash8 } from "crypto";
|
|
21272
21324
|
var TESTERS_ISSUE_REPORT_SCHEMA_VERSION = "testers.issue_report.v1";
|
|
21273
21325
|
var TESTERS_ISSUE_REPORT_RESULT_SCHEMA_VERSION = "todos.tester_issue_report_result.v1";
|
|
21274
21326
|
var TESTERS_ISSUE_REPORT_BATCH_RESULT_SCHEMA_VERSION = "todos.tester_issue_report_batch_result.v1";
|
|
@@ -21457,7 +21509,7 @@ function fingerprintTesterIssueReport(report) {
|
|
|
21457
21509
|
normalizeText2(report.failure?.message || report.summary || report.title).slice(0, 240),
|
|
21458
21510
|
normalizeText2(stackTop).slice(0, 160)
|
|
21459
21511
|
].join("::");
|
|
21460
|
-
return `testers:${
|
|
21512
|
+
return `testers:${createHash8("sha256").update(raw).digest("hex").slice(0, 16)}`;
|
|
21461
21513
|
}
|
|
21462
21514
|
function priorityForSeverity(severity, fallback) {
|
|
21463
21515
|
return PRIORITIES3.includes(severity) ? severity : fallback;
|
|
@@ -22909,7 +22961,7 @@ function renderLocalReportMarkdown(report) {
|
|
|
22909
22961
|
init_config2();
|
|
22910
22962
|
init_redaction();
|
|
22911
22963
|
init_prewrite_secrets();
|
|
22912
|
-
import { createCipheriv, createDecipheriv, createHash as
|
|
22964
|
+
import { createCipheriv, createDecipheriv, createHash as createHash9, randomBytes, scryptSync, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
22913
22965
|
var TODOS_ENCRYPTED_VALUE_KIND = "hasna.todos.encrypted-value";
|
|
22914
22966
|
var TODOS_ENCRYPTED_BRIDGE_KIND = "hasna.todos.encrypted-bridge";
|
|
22915
22967
|
var TODOS_ENCRYPTION_SCHEMA_VERSION = 1;
|
|
@@ -22935,7 +22987,7 @@ function now3() {
|
|
|
22935
22987
|
return new Date().toISOString();
|
|
22936
22988
|
}
|
|
22937
22989
|
function sha2564(value) {
|
|
22938
|
-
return
|
|
22990
|
+
return createHash9("sha256").update(value).digest("hex");
|
|
22939
22991
|
}
|
|
22940
22992
|
function normalizeProfileName(value) {
|
|
22941
22993
|
const name = (value || DEFAULT_ENCRYPTION_PROFILE).trim();
|
|
@@ -28249,7 +28301,7 @@ function isCommentRedactionBackfillComplete(result) {
|
|
|
28249
28301
|
return !result.dry_run && result.conflicts === 0 && result.remaining_candidates === 0;
|
|
28250
28302
|
}
|
|
28251
28303
|
// src/storage/s3-artifacts.ts
|
|
28252
|
-
import { createHash as
|
|
28304
|
+
import { createHash as createHash10, createHmac as createHmac2 } from "crypto";
|
|
28253
28305
|
function createTodosS3ArtifactStore(options) {
|
|
28254
28306
|
const requestFetch = options.fetch ?? fetch;
|
|
28255
28307
|
const now4 = options.now ?? (() => new Date);
|
|
@@ -28421,7 +28473,7 @@ function toAmzDate(date) {
|
|
|
28421
28473
|
return date.toISOString().replace(/[:-]|\.\d{3}/g, "");
|
|
28422
28474
|
}
|
|
28423
28475
|
function sha256Hex(value) {
|
|
28424
|
-
return
|
|
28476
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
28425
28477
|
}
|
|
28426
28478
|
function hmac(key, value) {
|
|
28427
28479
|
return createHmac2("sha256", key).update(value).digest();
|
|
@@ -29268,7 +29320,7 @@ function createClient(options) {
|
|
|
29268
29320
|
init_database();
|
|
29269
29321
|
|
|
29270
29322
|
// src/pr-groups/ledger.ts
|
|
29271
|
-
import { createHash as
|
|
29323
|
+
import { createHash as createHash11 } from "crypto";
|
|
29272
29324
|
|
|
29273
29325
|
// src/pr-groups/types.ts
|
|
29274
29326
|
var PR_GROUP_LEDGER_SCHEMA_VERSION = 1;
|
|
@@ -29331,7 +29383,7 @@ var RECEIPT_EVENT_TYPES = new Set([
|
|
|
29331
29383
|
"merge_outcome"
|
|
29332
29384
|
]);
|
|
29333
29385
|
function sha2565(value) {
|
|
29334
|
-
return
|
|
29386
|
+
return createHash11("sha256").update(value).digest("hex");
|
|
29335
29387
|
}
|
|
29336
29388
|
function stableValue(value) {
|
|
29337
29389
|
if (Array.isArray(value))
|
|
@@ -31849,7 +31901,7 @@ init_task_lifecycle();
|
|
|
31849
31901
|
init_task_crud();
|
|
31850
31902
|
init_redaction();
|
|
31851
31903
|
import { Database as Database3 } from "bun:sqlite";
|
|
31852
|
-
import { createHash as
|
|
31904
|
+
import { createHash as createHash12 } from "crypto";
|
|
31853
31905
|
import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
31854
31906
|
import { basename as basename2, dirname as dirname6, join as join9, resolve as resolve10 } from "path";
|
|
31855
31907
|
|
|
@@ -32132,7 +32184,7 @@ function normalizePath3(input) {
|
|
|
32132
32184
|
return resolve10(input);
|
|
32133
32185
|
}
|
|
32134
32186
|
function sourceStoreId(sourceDbPath) {
|
|
32135
|
-
const digest =
|
|
32187
|
+
const digest = createHash12("sha256").update(sourceDbPath).digest("hex").slice(0, 16);
|
|
32136
32188
|
return `sqlite:${digest}`;
|
|
32137
32189
|
}
|
|
32138
32190
|
function inferSourceRepoPath(sourceDbPath) {
|
|
@@ -34094,7 +34146,7 @@ init_comments();
|
|
|
34094
34146
|
|
|
34095
34147
|
// src/db/api-keys.ts
|
|
34096
34148
|
init_database();
|
|
34097
|
-
import { createHash as
|
|
34149
|
+
import { createHash as createHash13, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
34098
34150
|
function rowToRecord(row) {
|
|
34099
34151
|
return {
|
|
34100
34152
|
id: row.id,
|
|
@@ -34108,7 +34160,7 @@ function rowToRecord(row) {
|
|
|
34108
34160
|
};
|
|
34109
34161
|
}
|
|
34110
34162
|
function hashApiKey(key) {
|
|
34111
|
-
return
|
|
34163
|
+
return createHash13("sha256").update(key).digest("hex");
|
|
34112
34164
|
}
|
|
34113
34165
|
function safeEqualHex(a, b) {
|
|
34114
34166
|
if (a.length !== b.length)
|
|
@@ -38553,7 +38605,7 @@ init_database();
|
|
|
38553
38605
|
init_tasks();
|
|
38554
38606
|
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
38555
38607
|
import { basename as basename5 } from "path";
|
|
38556
|
-
import { createHash as
|
|
38608
|
+
import { createHash as createHash14 } from "crypto";
|
|
38557
38609
|
init_secret_redaction();
|
|
38558
38610
|
var INBOX_INTAKE_SCHEMA = "todos.inbox_intake.v1";
|
|
38559
38611
|
var INTAKE_SOURCE_TYPES = [
|
|
@@ -38566,7 +38618,7 @@ var INTAKE_SOURCE_TYPES = [
|
|
|
38566
38618
|
];
|
|
38567
38619
|
var INTAKE_TRIAGE_STATUSES = ["preview", "triaged", "duplicate", "created"];
|
|
38568
38620
|
function fingerprint2(text) {
|
|
38569
|
-
return
|
|
38621
|
+
return createHash14("sha256").update(text).digest("hex").slice(0, 16);
|
|
38570
38622
|
}
|
|
38571
38623
|
function loadRawContent(input) {
|
|
38572
38624
|
if (input.github_url) {
|
|
@@ -43106,7 +43158,7 @@ var CLI_COMMAND_GROUPS = [
|
|
|
43106
43158
|
commands: [
|
|
43107
43159
|
{ name: "completion", summary: "Shell completions", usage: "todos completion <bash|zsh|fish>", example: "todos completion bash >> ~/.bashrc" },
|
|
43108
43160
|
{ name: "docs", summary: "CLI reference and adapter docs", example: "todos docs cli" },
|
|
43109
|
-
{ name: "mcp", summary: "Register MCP server", flags: ["--
|
|
43161
|
+
{ name: "mcp", summary: "Register MCP server", flags: ["--register <agent>", "--unregister <agent>", "--global"] }
|
|
43110
43162
|
]
|
|
43111
43163
|
}
|
|
43112
43164
|
];
|
|
@@ -44026,7 +44078,7 @@ init_database();
|
|
|
44026
44078
|
init_tasks();
|
|
44027
44079
|
init_redaction();
|
|
44028
44080
|
init_sync_utils();
|
|
44029
|
-
import { createHash as
|
|
44081
|
+
import { createHash as createHash15 } from "crypto";
|
|
44030
44082
|
import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
|
|
44031
44083
|
import { hostname as hostname3, platform, arch } from "os";
|
|
44032
44084
|
import { dirname as dirname15, join as join18, resolve as resolve17 } from "path";
|
|
@@ -44048,7 +44100,7 @@ var CONFIG_FILES = [
|
|
|
44048
44100
|
"dashboard/vite.config.ts"
|
|
44049
44101
|
];
|
|
44050
44102
|
function sha2566(value) {
|
|
44051
|
-
return
|
|
44103
|
+
return createHash15("sha256").update(value).digest("hex");
|
|
44052
44104
|
}
|
|
44053
44105
|
function fileRecord(root, relativePath) {
|
|
44054
44106
|
const path = join18(root, relativePath);
|
|
@@ -44300,7 +44352,7 @@ function compareEnvironmentSnapshotFiles(leftPath, rightPath) {
|
|
|
44300
44352
|
init_database();
|
|
44301
44353
|
init_projects();
|
|
44302
44354
|
init_plans();
|
|
44303
|
-
import { createHash as
|
|
44355
|
+
import { createHash as createHash16 } from "crypto";
|
|
44304
44356
|
import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
|
|
44305
44357
|
import { dirname as dirname16, join as join19 } from "path";
|
|
44306
44358
|
var DECISION_RECORD_SCHEMA = "todos.decision_record.v1";
|
|
@@ -44356,7 +44408,7 @@ function rowToDecisionRecord(row) {
|
|
|
44356
44408
|
}
|
|
44357
44409
|
function stableSnapshotHash(payload) {
|
|
44358
44410
|
const { captured_at: _capturedAt, ...rest } = payload;
|
|
44359
|
-
return
|
|
44411
|
+
return createHash16("sha256").update(JSON.stringify(rest)).digest("hex");
|
|
44360
44412
|
}
|
|
44361
44413
|
function createDecisionRecord(input, db) {
|
|
44362
44414
|
const d = db || getDatabase();
|
|
@@ -48405,15 +48457,17 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
48405
48457
|
const lastSyncedAt = parseTimestamp(existing.task.metadata?.["todos_updated_at"]);
|
|
48406
48458
|
const localUpdatedAt = parseTimestamp(task2.updated_at);
|
|
48407
48459
|
const remoteUpdatedAt = existing.mtimeMs;
|
|
48460
|
+
const remoteChanged = hasSyncFingerprintChanged(existing.task);
|
|
48461
|
+
const remoteChangedSinceSync = remoteChanged ?? Boolean(lastSyncedAt && remoteUpdatedAt && remoteUpdatedAt > lastSyncedAt);
|
|
48408
48462
|
let recordConflict = false;
|
|
48409
|
-
if (lastSyncedAt && localUpdatedAt &&
|
|
48463
|
+
if (lastSyncedAt && localUpdatedAt && localUpdatedAt > lastSyncedAt && remoteChangedSinceSync) {
|
|
48410
48464
|
if (prefer === "remote") {
|
|
48411
48465
|
const conflict = {
|
|
48412
48466
|
agent: "claude",
|
|
48413
48467
|
direction: "push",
|
|
48414
48468
|
prefer,
|
|
48415
48469
|
local_updated_at: task2.updated_at,
|
|
48416
|
-
remote_updated_at: new Date(remoteUpdatedAt).toISOString(),
|
|
48470
|
+
remote_updated_at: remoteUpdatedAt ? new Date(remoteUpdatedAt).toISOString() : undefined,
|
|
48417
48471
|
detected_at: new Date().toISOString()
|
|
48418
48472
|
};
|
|
48419
48473
|
const newMeta = appendSyncConflict(task2.metadata, conflict);
|
|
@@ -48427,7 +48481,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
48427
48481
|
updated.blocks = existing.task.blocks;
|
|
48428
48482
|
updated.blockedBy = existing.task.blockedBy;
|
|
48429
48483
|
updated.activeForm = existing.task.activeForm;
|
|
48430
|
-
writeClaudeTask(dir, updated);
|
|
48484
|
+
writeClaudeTask(dir, withSyncFingerprint(updated));
|
|
48431
48485
|
if (recordConflict) {
|
|
48432
48486
|
const latest = getTask(task2.id);
|
|
48433
48487
|
if (latest) {
|
|
@@ -48451,7 +48505,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
48451
48505
|
prefixCounter++;
|
|
48452
48506
|
ct.subject = formatPrefixedSubject(task2.title, prefixConfig.prefix, prefixCounter);
|
|
48453
48507
|
}
|
|
48454
|
-
writeClaudeTask(dir, ct);
|
|
48508
|
+
writeClaudeTask(dir, withSyncFingerprint(ct));
|
|
48455
48509
|
const current = getTask(task2.id);
|
|
48456
48510
|
if (current) {
|
|
48457
48511
|
const newMeta = { ...current.metadata, claude_task_id: claudeId };
|
|
@@ -48577,7 +48631,7 @@ function writeAgentTask(dir, task2) {
|
|
|
48577
48631
|
writeJsonFile(join24(dir, `${task2.id}.json`), task2);
|
|
48578
48632
|
}
|
|
48579
48633
|
function taskToAgentTask(task2, externalId, existingMeta) {
|
|
48580
|
-
return {
|
|
48634
|
+
return withSyncFingerprint({
|
|
48581
48635
|
id: externalId,
|
|
48582
48636
|
title: task2.title,
|
|
48583
48637
|
description: task2.description || "",
|
|
@@ -48592,7 +48646,7 @@ function taskToAgentTask(task2, externalId, existingMeta) {
|
|
|
48592
48646
|
todos_updated_at: task2.updated_at,
|
|
48593
48647
|
todos_version: task2.version
|
|
48594
48648
|
}
|
|
48595
|
-
};
|
|
48649
|
+
});
|
|
48596
48650
|
}
|
|
48597
48651
|
function metadataKey(agent) {
|
|
48598
48652
|
return `${agent}_task_id`;
|
|
@@ -48626,15 +48680,17 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
48626
48680
|
const lastSyncedAt = parseTimestamp(existing.task.metadata?.["todos_updated_at"]);
|
|
48627
48681
|
const localUpdatedAt = parseTimestamp(task2.updated_at);
|
|
48628
48682
|
const remoteUpdatedAt = existing.mtimeMs;
|
|
48683
|
+
const remoteChanged = hasSyncFingerprintChanged(existing.task);
|
|
48684
|
+
const remoteChangedSinceSync = remoteChanged ?? Boolean(lastSyncedAt && remoteUpdatedAt && remoteUpdatedAt > lastSyncedAt);
|
|
48629
48685
|
let recordConflict = false;
|
|
48630
|
-
if (lastSyncedAt && localUpdatedAt &&
|
|
48686
|
+
if (lastSyncedAt && localUpdatedAt && localUpdatedAt > lastSyncedAt && remoteChangedSinceSync) {
|
|
48631
48687
|
if (prefer === "remote") {
|
|
48632
48688
|
const conflict = {
|
|
48633
48689
|
agent,
|
|
48634
48690
|
direction: "push",
|
|
48635
48691
|
prefer,
|
|
48636
48692
|
local_updated_at: task2.updated_at,
|
|
48637
|
-
remote_updated_at: new Date(remoteUpdatedAt).toISOString(),
|
|
48693
|
+
remote_updated_at: remoteUpdatedAt ? new Date(remoteUpdatedAt).toISOString() : undefined,
|
|
48638
48694
|
detected_at: new Date().toISOString()
|
|
48639
48695
|
};
|
|
48640
48696
|
const newMeta = appendSyncConflict(task2.metadata, conflict);
|
|
@@ -48842,7 +48898,7 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
|
|
|
48842
48898
|
init_tasks();
|
|
48843
48899
|
init_task_files();
|
|
48844
48900
|
import { existsSync as existsSync27, readFileSync as readFileSync23, statSync as statSync10 } from "fs";
|
|
48845
|
-
import { createHash as
|
|
48901
|
+
import { createHash as createHash17 } from "crypto";
|
|
48846
48902
|
import { relative as relative6, resolve as resolve18, join as join25 } from "path";
|
|
48847
48903
|
var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
|
|
48848
48904
|
var DEFAULT_EXTENSIONS = new Set([
|
|
@@ -48907,7 +48963,7 @@ var SKIP_DIRS2 = new Set([
|
|
|
48907
48963
|
".parcel-cache"
|
|
48908
48964
|
]);
|
|
48909
48965
|
function stableHash(value) {
|
|
48910
|
-
return
|
|
48966
|
+
return createHash17("sha256").update(value).digest("hex");
|
|
48911
48967
|
}
|
|
48912
48968
|
function normalizePathForMatch(value) {
|
|
48913
48969
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -49803,7 +49859,7 @@ function renderWorkflowStatesMarkdown(states = listWorkflowStates()) {
|
|
|
49803
49859
|
}
|
|
49804
49860
|
// src/lib/agent-replay-simulator.ts
|
|
49805
49861
|
init_redaction();
|
|
49806
|
-
import { createHash as
|
|
49862
|
+
import { createHash as createHash18 } from "crypto";
|
|
49807
49863
|
import { readFileSync as readFileSync24 } from "fs";
|
|
49808
49864
|
function isObject(value) {
|
|
49809
49865
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -49825,7 +49881,7 @@ function stable2(value) {
|
|
|
49825
49881
|
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable2(value[key])]));
|
|
49826
49882
|
}
|
|
49827
49883
|
function fingerprint3(value) {
|
|
49828
|
-
return
|
|
49884
|
+
return createHash18("sha256").update(JSON.stringify(stable2(value))).digest("hex");
|
|
49829
49885
|
}
|
|
49830
49886
|
function unpackFixture(input) {
|
|
49831
49887
|
if (!isObject(input))
|
|
@@ -50064,7 +50120,7 @@ function renderAgentReplaySimulationMarkdown(simulation) {
|
|
|
50064
50120
|
}
|
|
50065
50121
|
// src/lib/local-extensions.ts
|
|
50066
50122
|
init_config2();
|
|
50067
|
-
import { createHash as
|
|
50123
|
+
import { createHash as createHash19, createVerify } from "crypto";
|
|
50068
50124
|
import { existsSync as existsSync28, readdirSync as readdirSync6, readFileSync as readFileSync25, statSync as statSync11 } from "fs";
|
|
50069
50125
|
import { basename as basename6, join as join26, resolve as resolve19 } from "path";
|
|
50070
50126
|
init_redaction();
|
|
@@ -50152,7 +50208,7 @@ function parseJson2(path) {
|
|
|
50152
50208
|
return JSON.parse(readFileSync25(path, "utf8"));
|
|
50153
50209
|
}
|
|
50154
50210
|
function sha2567(bytes) {
|
|
50155
|
-
return `sha256:${
|
|
50211
|
+
return `sha256:${createHash19("sha256").update(bytes).digest("hex")}`;
|
|
50156
50212
|
}
|
|
50157
50213
|
function compareVersions(a, b) {
|
|
50158
50214
|
const left = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-tasks.d.ts","sourceRoot":"","sources":["../../src/lib/agent-tasks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent-tasks.d.ts","sourceRoot":"","sources":["../../src/lib/agent-tasks.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAwD9D,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CA2FZ;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAoFZ;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAQZ"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the {@link AssigneeContext} that `validateAssignee` consumes, and
|
|
3
|
+
* caches it briefly.
|
|
4
|
+
*
|
|
5
|
+
* `assignee-validation.ts` is deliberately pure and does no IO. This module is
|
|
6
|
+
* the IO half, kept separate so the validator stays trivially testable.
|
|
7
|
+
*
|
|
8
|
+
* WHY THE CACHE EXISTS. Validation needs the agent roster, and on this fleet
|
|
9
|
+
* that is a ~710KB / ~2.4s fetch against the cloud store. The one-shot CLI pays
|
|
10
|
+
* it once and does not care. The MCP server is LONG-LIVED and validates on
|
|
11
|
+
* every create/update/upsert, so a 50-task bulk upsert — the loop-driven path —
|
|
12
|
+
* would refetch ~35MB. Found in adversarial review of todos 056f3597.
|
|
13
|
+
*
|
|
14
|
+
* The TTL is deliberately short. An agent that registers during the window is
|
|
15
|
+
* invisible for at most that long, and the only consequence is a WARNING on an
|
|
16
|
+
* assignee that is in fact valid — never a refusal, because the refusing tiers
|
|
17
|
+
* (seat, ambiguous) cannot be created by a registration the cache missed:
|
|
18
|
+
* a new seat requires an owner ruling and a roster edit, and a name becoming
|
|
19
|
+
* ambiguous only ever ADDS a row, which at worst delays the refusal by the TTL.
|
|
20
|
+
*/
|
|
21
|
+
import type { AssigneeContext } from "./assignee-validation.js";
|
|
22
|
+
/** Drop the cache. For tests, and for any caller that has just mutated agents. */
|
|
23
|
+
export declare function resetAssigneeContextCache(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Fetch (or reuse) the agent roster and seat list.
|
|
26
|
+
*
|
|
27
|
+
* `listAgentsFn` is injected rather than imported so this module does not pull
|
|
28
|
+
* `bun:sqlite` into every consumer's module graph — the same constraint that
|
|
29
|
+
* keeps `agent-name-normalize.ts` import-free.
|
|
30
|
+
*/
|
|
31
|
+
export declare function loadAssigneeContext(listAgentsFn: () => Promise<Array<{
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
}>> | Array<{
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
}>, allowSeat: boolean, nowMs?: number): Promise<AssigneeContext & {
|
|
38
|
+
degraded: boolean;
|
|
39
|
+
}>;
|
|
40
|
+
//# sourceMappingURL=assignee-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignee-context.d.ts","sourceRoot":"","sources":["../../src/lib/assignee-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAc,MAAM,0BAA0B,CAAC;AAgB5E,kFAAkF;AAClF,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,YAAY,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,GAAG,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EACtG,SAAS,EAAE,OAAO,EAClB,KAAK,GAAE,MAAmB,GACzB,OAAO,CAAC,eAAe,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAkClD"}
|