@hasna/todos 0.15.50 → 0.15.52
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/cloud-router.d.ts.map +1 -1
- package/dist/cli/commands/project-commands.d.ts +15 -0
- package/dist/cli/commands/project-commands.d.ts.map +1 -1
- package/dist/cli/commands/query-commands.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +521 -342
- package/dist/contracts.js +194 -95
- package/dist/db/task-crud.d.ts.map +1 -1
- package/dist/index.js +406 -299
- package/dist/lib/model-config.d.ts +2 -1
- package/dist/lib/model-config.d.ts.map +1 -1
- package/dist/lib/paths.d.ts +40 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/sync-utils.d.ts +7 -0
- package/dist/lib/sync-utils.d.ts.map +1 -1
- package/dist/mcp/index.js +280 -174
- package/dist/mcp.js +7 -4
- package/dist/project-registration.js +192 -93
- package/dist/registry.js +194 -95
- package/dist/release-provenance.json +5 -5
- package/dist/sdk/index.js +100 -8
- package/dist/server/index.js +449 -220
- package/dist/storage.js +183 -87
- package/dist/task-manifest.js +113 -17
- package/dist/testing.d.ts +10 -9
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +7 -4
- package/postinstall.js +34 -0
package/dist/server/index.js
CHANGED
|
@@ -70,7 +70,7 @@ var package_default;
|
|
|
70
70
|
var init_package = __esm(() => {
|
|
71
71
|
package_default = {
|
|
72
72
|
name: "@hasna/todos",
|
|
73
|
-
version: "0.15.
|
|
73
|
+
version: "0.15.52",
|
|
74
74
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
75
75
|
type: "module",
|
|
76
76
|
main: "dist/index.js",
|
|
@@ -129,6 +129,7 @@ var init_package = __esm(() => {
|
|
|
129
129
|
files: [
|
|
130
130
|
"dist",
|
|
131
131
|
"dashboard/dist",
|
|
132
|
+
"postinstall.js",
|
|
132
133
|
"LICENSE",
|
|
133
134
|
"README.md"
|
|
134
135
|
],
|
|
@@ -155,7 +156,7 @@ var init_package = __esm(() => {
|
|
|
155
156
|
"test:attested-container-compatibility-vector": "bun test scripts/attested-container-compatibility-vector.test.ts",
|
|
156
157
|
"issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
|
|
157
158
|
prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
|
|
158
|
-
postinstall: "
|
|
159
|
+
postinstall: "node postinstall.js"
|
|
159
160
|
},
|
|
160
161
|
keywords: [
|
|
161
162
|
"todos",
|
|
@@ -189,13 +190,15 @@ var init_package = __esm(() => {
|
|
|
189
190
|
author: "Andrei Hasna <andrei@hasna.com>",
|
|
190
191
|
license: "Apache-2.0",
|
|
191
192
|
dependencies: {
|
|
192
|
-
"@hasna/contracts": "0.14.
|
|
193
|
+
"@hasna/contracts": "0.14.2",
|
|
193
194
|
"@hasna/events": "^0.1.11",
|
|
195
|
+
"@hasna/paths": "0.1.0",
|
|
194
196
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
195
197
|
chalk: "^5.4.1",
|
|
196
198
|
commander: "^13.1.0",
|
|
197
199
|
ink: "^5.2.0",
|
|
198
200
|
react: "^18.3.1",
|
|
201
|
+
"signal-exit": "3.0.7",
|
|
199
202
|
zod: "3.25.76"
|
|
200
203
|
},
|
|
201
204
|
overrides: {
|
|
@@ -204,7 +207,7 @@ var init_package = __esm(() => {
|
|
|
204
207
|
zod: "3.25.76"
|
|
205
208
|
},
|
|
206
209
|
devDependencies: {
|
|
207
|
-
"@types/bun": "
|
|
210
|
+
"@types/bun": "1.3.14",
|
|
208
211
|
"@types/react": "^18.3.18",
|
|
209
212
|
"bun-types": "1.3.9",
|
|
210
213
|
"hasna-deployment-contracts": "npm:@hasna/contracts@0.10.4",
|
|
@@ -225,7 +228,7 @@ var init_package_version = __esm(() => {
|
|
|
225
228
|
init_package();
|
|
226
229
|
});
|
|
227
230
|
|
|
228
|
-
// node_modules/.bun/@hasna+contracts@0.14.
|
|
231
|
+
// node_modules/.bun/@hasna+contracts@0.14.2+a5fb9613d5af3331/node_modules/@hasna/contracts/dist/auth/index.js
|
|
229
232
|
import { createHash, createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
230
233
|
function isValidTenantId(value) {
|
|
231
234
|
return typeof value === "string" && TENANT_ID_PATTERN.test(value);
|
|
@@ -1308,18 +1311,112 @@ function normalizeAgentNameInput(name) {
|
|
|
1308
1311
|
return name.trim().toLowerCase();
|
|
1309
1312
|
}
|
|
1310
1313
|
|
|
1311
|
-
//
|
|
1312
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
1314
|
+
// node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
|
|
1313
1315
|
import { homedir } from "os";
|
|
1314
1316
|
import { join } from "path";
|
|
1317
|
+
function assertApp(app) {
|
|
1318
|
+
if (typeof app !== "string" || app.length === 0) {
|
|
1319
|
+
throw new TypeError("paths: app must be a non-empty string");
|
|
1320
|
+
}
|
|
1321
|
+
if (!APP_SLUG_RE.test(app)) {
|
|
1322
|
+
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
function envOf(options) {
|
|
1326
|
+
return options.env ?? process.env;
|
|
1327
|
+
}
|
|
1328
|
+
function envValue(options, kind) {
|
|
1329
|
+
const value = envOf(options)[KIND_ENV[kind]];
|
|
1330
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
1331
|
+
}
|
|
1332
|
+
function isMacOS(platform) {
|
|
1333
|
+
return platform === "darwin";
|
|
1334
|
+
}
|
|
1335
|
+
function baseDir(kind, options) {
|
|
1336
|
+
const override = envValue(options, kind);
|
|
1337
|
+
if (override)
|
|
1338
|
+
return override;
|
|
1339
|
+
const home = options.home ?? homedir();
|
|
1340
|
+
const platform = options.platform ?? process.platform;
|
|
1341
|
+
if (isMacOS(platform)) {
|
|
1342
|
+
switch (kind) {
|
|
1343
|
+
case "config":
|
|
1344
|
+
case "data":
|
|
1345
|
+
return join(home, "Library", "Application Support", "Hasna");
|
|
1346
|
+
case "cache":
|
|
1347
|
+
return join(home, "Library", "Caches", "Hasna");
|
|
1348
|
+
case "state":
|
|
1349
|
+
return join(home, "Library", "Logs", "Hasna");
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
switch (kind) {
|
|
1353
|
+
case "config":
|
|
1354
|
+
return join(home, ".config", "hasna");
|
|
1355
|
+
case "data":
|
|
1356
|
+
return join(home, ".local", "share", "hasna");
|
|
1357
|
+
case "state":
|
|
1358
|
+
return join(home, ".local", "state", "hasna");
|
|
1359
|
+
case "cache":
|
|
1360
|
+
return join(home, ".cache", "hasna");
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
function resolvePath(kind, options) {
|
|
1364
|
+
assertApp(options.app);
|
|
1365
|
+
const appSegment = options.internal === true ? join("internal", options.app) : options.app;
|
|
1366
|
+
return join(baseDir(kind, options), appSegment);
|
|
1367
|
+
}
|
|
1368
|
+
function dataDir(options) {
|
|
1369
|
+
return resolvePath("data", options);
|
|
1370
|
+
}
|
|
1371
|
+
var KIND_ENV, APP_SLUG_RE;
|
|
1372
|
+
var init_dist = __esm(() => {
|
|
1373
|
+
KIND_ENV = {
|
|
1374
|
+
config: "HASNA_CONFIG_HOME",
|
|
1375
|
+
data: "HASNA_DATA_HOME",
|
|
1376
|
+
state: "HASNA_STATE_HOME",
|
|
1377
|
+
cache: "HASNA_CACHE_HOME"
|
|
1378
|
+
};
|
|
1379
|
+
APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1380
|
+
});
|
|
1381
|
+
|
|
1382
|
+
// src/lib/paths.ts
|
|
1383
|
+
import { existsSync } from "fs";
|
|
1384
|
+
import { homedir as homedir2 } from "os";
|
|
1385
|
+
import { join as join2, resolve } from "path";
|
|
1386
|
+
function effectiveHome(env = process.env) {
|
|
1387
|
+
return env.HOME || env.USERPROFILE || homedir2();
|
|
1388
|
+
}
|
|
1389
|
+
function legacyHomeDir(env = process.env) {
|
|
1390
|
+
return join2(effectiveHome(env), ".hasna", "todos");
|
|
1391
|
+
}
|
|
1392
|
+
function resolverHome(env = process.env) {
|
|
1393
|
+
return dataDir({ app: "todos", home: effectiveHome(env), env });
|
|
1394
|
+
}
|
|
1395
|
+
function adoptResolverHome(resolved, env = process.env) {
|
|
1396
|
+
const dataOverride = env.HASNA_DATA_HOME;
|
|
1397
|
+
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
1398
|
+
return true;
|
|
1399
|
+
return existsSync(join2(resolved, "todos.db")) || existsSync(join2(resolved, "config.json"));
|
|
1400
|
+
}
|
|
1401
|
+
function getTodosDir(env = process.env) {
|
|
1402
|
+
const resolved = resolverHome(env);
|
|
1403
|
+
return resolve(adoptResolverHome(resolved, env) ? resolved : legacyHomeDir(env));
|
|
1404
|
+
}
|
|
1405
|
+
var init_paths = __esm(() => {
|
|
1406
|
+
init_dist();
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
// src/lib/sync-utils.ts
|
|
1410
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
1411
|
+
import { homedir as homedir3 } from "os";
|
|
1315
1412
|
function getHomeDir() {
|
|
1316
|
-
return process.env["HOME"] || process.env["USERPROFILE"] ||
|
|
1413
|
+
return process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
|
|
1317
1414
|
}
|
|
1318
1415
|
function getTodosGlobalDir() {
|
|
1319
|
-
return
|
|
1416
|
+
return getTodosDir();
|
|
1320
1417
|
}
|
|
1321
1418
|
function ensureDir(dir) {
|
|
1322
|
-
if (!
|
|
1419
|
+
if (!existsSync2(dir))
|
|
1323
1420
|
mkdirSync(dir, { recursive: true });
|
|
1324
1421
|
}
|
|
1325
1422
|
function readJsonFile(path) {
|
|
@@ -1340,18 +1437,19 @@ function appendSyncConflict(metadata, conflict, limit = 5) {
|
|
|
1340
1437
|
}
|
|
1341
1438
|
var HOME;
|
|
1342
1439
|
var init_sync_utils = __esm(() => {
|
|
1440
|
+
init_paths();
|
|
1343
1441
|
HOME = getHomeDir();
|
|
1344
1442
|
});
|
|
1345
1443
|
|
|
1346
1444
|
// src/lib/creator-identity.ts
|
|
1347
|
-
import { existsSync as
|
|
1348
|
-
import { join as
|
|
1445
|
+
import { existsSync as existsSync3, rmSync } from "fs";
|
|
1446
|
+
import { join as join3 } from "path";
|
|
1349
1447
|
function identityFilePath() {
|
|
1350
|
-
return
|
|
1448
|
+
return join3(getTodosGlobalDir(), "identity.json");
|
|
1351
1449
|
}
|
|
1352
1450
|
function readPersistedIdentity() {
|
|
1353
1451
|
const path = identityFilePath();
|
|
1354
|
-
if (!
|
|
1452
|
+
if (!existsSync3(path))
|
|
1355
1453
|
return null;
|
|
1356
1454
|
const parsed = readJsonFile(path);
|
|
1357
1455
|
if (!parsed || typeof parsed.agent_id !== "string" || !parsed.agent_id.trim())
|
|
@@ -1499,15 +1597,15 @@ var init_plan_project_link_contract = __esm(() => {
|
|
|
1499
1597
|
});
|
|
1500
1598
|
|
|
1501
1599
|
// src/lib/config.ts
|
|
1502
|
-
import { existsSync as
|
|
1503
|
-
import { dirname, join as
|
|
1600
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
1601
|
+
import { dirname, join as join4 } from "path";
|
|
1504
1602
|
function getConfigPath() {
|
|
1505
|
-
return
|
|
1603
|
+
return join4(getTodosGlobalDir(), "config.json");
|
|
1506
1604
|
}
|
|
1507
1605
|
function loadConfig() {
|
|
1508
1606
|
if (cached)
|
|
1509
1607
|
return cached;
|
|
1510
|
-
if (!
|
|
1608
|
+
if (!existsSync4(getConfigPath())) {
|
|
1511
1609
|
cached = {};
|
|
1512
1610
|
return cached;
|
|
1513
1611
|
}
|
|
@@ -5357,7 +5455,7 @@ async function retryOnTransientPostgresError(fn, attempts = 2, delayMs = 150) {
|
|
|
5357
5455
|
lastError = error;
|
|
5358
5456
|
if (!isTransientPostgresError(error) || attempt === attempts)
|
|
5359
5457
|
throw error;
|
|
5360
|
-
await new Promise((
|
|
5458
|
+
await new Promise((resolve2) => setTimeout(resolve2, delayMs * attempt));
|
|
5361
5459
|
}
|
|
5362
5460
|
}
|
|
5363
5461
|
throw lastError;
|
|
@@ -11134,9 +11232,9 @@ var init_schema = __esm(() => {
|
|
|
11134
11232
|
});
|
|
11135
11233
|
|
|
11136
11234
|
// src/db/machines.ts
|
|
11137
|
-
import { existsSync as
|
|
11235
|
+
import { existsSync as existsSync5 } from "fs";
|
|
11138
11236
|
import { hostname as osHostname, platform as osPlatform, arch as osArch } from "os";
|
|
11139
|
-
import { resolve } from "path";
|
|
11237
|
+
import { resolve as resolve2 } from "path";
|
|
11140
11238
|
import { spawnSync } from "child_process";
|
|
11141
11239
|
function parseMetadata(value) {
|
|
11142
11240
|
if (!value)
|
|
@@ -11167,7 +11265,7 @@ function discoverGitRoot(workspacePath) {
|
|
|
11167
11265
|
}
|
|
11168
11266
|
function topologyMetadata(input, existing = {}) {
|
|
11169
11267
|
const next = { ...existing };
|
|
11170
|
-
const workspacePath = input.workspace_path ?
|
|
11268
|
+
const workspacePath = input.workspace_path ? resolve2(input.workspace_path) : undefined;
|
|
11171
11269
|
const entries = {
|
|
11172
11270
|
tailscale_name: input.tailscale_name,
|
|
11173
11271
|
tailscale_ip: input.tailscale_ip,
|
|
@@ -11341,7 +11439,7 @@ function getMachineTopologyDiagnostics(opts = {}, db, at = new Date) {
|
|
|
11341
11439
|
message: `${project.name} has ${distinctPaths.length} different machine-local paths`
|
|
11342
11440
|
});
|
|
11343
11441
|
}
|
|
11344
|
-
if (localRow && !
|
|
11442
|
+
if (localRow && !existsSync5(localRow.path)) {
|
|
11345
11443
|
pathIssues.push({
|
|
11346
11444
|
type: "path_missing",
|
|
11347
11445
|
project_id: project.id,
|
|
@@ -11352,7 +11450,7 @@ function getMachineTopologyDiagnostics(opts = {}, db, at = new Date) {
|
|
|
11352
11450
|
message: `Local path does not exist on this machine: ${localRow.path}`
|
|
11353
11451
|
});
|
|
11354
11452
|
}
|
|
11355
|
-
if (!localRow && project.path && machineById.has(localMachine.id) && !
|
|
11453
|
+
if (!localRow && project.path && machineById.has(localMachine.id) && !existsSync5(project.path)) {
|
|
11356
11454
|
pathIssues.push({
|
|
11357
11455
|
type: "path_missing",
|
|
11358
11456
|
project_id: project.id,
|
|
@@ -11729,18 +11827,18 @@ __export(exports_database, {
|
|
|
11729
11827
|
LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
|
|
11730
11828
|
});
|
|
11731
11829
|
import { Database } from "bun:sqlite";
|
|
11732
|
-
import { existsSync as
|
|
11733
|
-
import { dirname as dirname2, join as
|
|
11830
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
11831
|
+
import { dirname as dirname2, join as join5, resolve as resolve3 } from "path";
|
|
11734
11832
|
function isInMemoryDb(path) {
|
|
11735
11833
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
11736
11834
|
}
|
|
11737
11835
|
function findNearestProjectDb(startDir) {
|
|
11738
11836
|
const gitRoot = findGitRoot(startDir);
|
|
11739
|
-
const stopAt = gitRoot ?
|
|
11740
|
-
let dir =
|
|
11837
|
+
const stopAt = gitRoot ? resolve3(gitRoot) : resolve3(startDir);
|
|
11838
|
+
let dir = resolve3(startDir);
|
|
11741
11839
|
while (true) {
|
|
11742
|
-
const candidate =
|
|
11743
|
-
if (
|
|
11840
|
+
const candidate = join5(dir, ".hasna", "todos", "todos.db");
|
|
11841
|
+
if (existsSync6(candidate))
|
|
11744
11842
|
return candidate;
|
|
11745
11843
|
if (dir === stopAt)
|
|
11746
11844
|
break;
|
|
@@ -11752,9 +11850,9 @@ function findNearestProjectDb(startDir) {
|
|
|
11752
11850
|
return null;
|
|
11753
11851
|
}
|
|
11754
11852
|
function findGitRoot(startDir) {
|
|
11755
|
-
let dir =
|
|
11853
|
+
let dir = resolve3(startDir);
|
|
11756
11854
|
while (true) {
|
|
11757
|
-
if (
|
|
11855
|
+
if (existsSync6(join5(dir, ".git")))
|
|
11758
11856
|
return dir;
|
|
11759
11857
|
const parent = dirname2(dir);
|
|
11760
11858
|
if (parent === dir)
|
|
@@ -11764,7 +11862,7 @@ function findGitRoot(startDir) {
|
|
|
11764
11862
|
return null;
|
|
11765
11863
|
}
|
|
11766
11864
|
function getGlobalDbPath() {
|
|
11767
|
-
return
|
|
11865
|
+
return join5(getTodosGlobalDir(), "todos.db");
|
|
11768
11866
|
}
|
|
11769
11867
|
function hasExplicitProjectArg(args = process.argv.slice(2)) {
|
|
11770
11868
|
return args.some((arg) => arg === "--project" || arg.startsWith("--project="));
|
|
@@ -11802,7 +11900,7 @@ function getDbPath() {
|
|
|
11802
11900
|
if (process.env["TODOS_DB_SCOPE"] === "project") {
|
|
11803
11901
|
const gitRoot = findGitRoot(cwd);
|
|
11804
11902
|
if (gitRoot && canCreateScopedProjectDb()) {
|
|
11805
|
-
return
|
|
11903
|
+
return join5(gitRoot, ".hasna", "todos", "todos.db");
|
|
11806
11904
|
}
|
|
11807
11905
|
}
|
|
11808
11906
|
return getGlobalDbPath();
|
|
@@ -11813,8 +11911,8 @@ function getDatabasePath() {
|
|
|
11813
11911
|
function ensureDir2(filePath) {
|
|
11814
11912
|
if (isInMemoryDb(filePath))
|
|
11815
11913
|
return;
|
|
11816
|
-
const dir = dirname2(
|
|
11817
|
-
if (!
|
|
11914
|
+
const dir = dirname2(resolve3(filePath));
|
|
11915
|
+
if (!existsSync6(dir)) {
|
|
11818
11916
|
mkdirSync2(dir, { recursive: true });
|
|
11819
11917
|
}
|
|
11820
11918
|
}
|
|
@@ -12653,14 +12751,14 @@ var init_completion_guard = __esm(() => {
|
|
|
12653
12751
|
|
|
12654
12752
|
// src/lib/event-emission-safety.ts
|
|
12655
12753
|
import { tmpdir } from "os";
|
|
12656
|
-
import { resolve as
|
|
12754
|
+
import { resolve as resolve4, sep } from "path";
|
|
12657
12755
|
function envFlag(name) {
|
|
12658
12756
|
const value = process.env[name]?.trim().toLowerCase();
|
|
12659
12757
|
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
12660
12758
|
}
|
|
12661
12759
|
function isUnder(parent, child) {
|
|
12662
|
-
const normalizedParent =
|
|
12663
|
-
const normalizedChild =
|
|
12760
|
+
const normalizedParent = resolve4(parent);
|
|
12761
|
+
const normalizedChild = resolve4(child);
|
|
12664
12762
|
return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}${sep}`);
|
|
12665
12763
|
}
|
|
12666
12764
|
function databasePathFromDatabase(db) {
|
|
@@ -12703,9 +12801,9 @@ var init_event_emission_safety = __esm(() => {
|
|
|
12703
12801
|
});
|
|
12704
12802
|
|
|
12705
12803
|
// src/lib/workspace-trust.ts
|
|
12706
|
-
import { relative, resolve as
|
|
12804
|
+
import { relative, resolve as resolve5 } from "path";
|
|
12707
12805
|
function normalizePath(path) {
|
|
12708
|
-
return
|
|
12806
|
+
return resolve5(path);
|
|
12709
12807
|
}
|
|
12710
12808
|
function unique2(values) {
|
|
12711
12809
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -12887,9 +12985,9 @@ var init_workspace_trust = __esm(() => {
|
|
|
12887
12985
|
});
|
|
12888
12986
|
|
|
12889
12987
|
// src/lib/runner-sandbox.ts
|
|
12890
|
-
import { relative as relative2, resolve as
|
|
12988
|
+
import { relative as relative2, resolve as resolve6 } from "path";
|
|
12891
12989
|
function normalizePath2(path) {
|
|
12892
|
-
return
|
|
12990
|
+
return resolve6(path);
|
|
12893
12991
|
}
|
|
12894
12992
|
function unique3(values) {
|
|
12895
12993
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -13079,7 +13177,7 @@ var init_runner_sandbox = __esm(() => {
|
|
|
13079
13177
|
// src/lib/event-hooks.ts
|
|
13080
13178
|
import { createHash as createHash5, randomUUID as randomUUID2 } from "crypto";
|
|
13081
13179
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
13082
|
-
import { dirname as dirname3, resolve as
|
|
13180
|
+
import { dirname as dirname3, resolve as resolve7 } from "path";
|
|
13083
13181
|
import { createConnection } from "net";
|
|
13084
13182
|
function safeName(name) {
|
|
13085
13183
|
const trimmed = name.trim();
|
|
@@ -13217,7 +13315,7 @@ async function deliverHook(hook, envelope) {
|
|
|
13217
13315
|
if (hook.target === "stdout") {
|
|
13218
13316
|
output = line.trim();
|
|
13219
13317
|
} else if (hook.target === "file") {
|
|
13220
|
-
const filePath =
|
|
13318
|
+
const filePath = resolve7(hook.file_path);
|
|
13221
13319
|
mkdirSync3(dirname3(filePath), { recursive: true });
|
|
13222
13320
|
appendFileSync(filePath, line);
|
|
13223
13321
|
} else if (hook.target === "socket") {
|
|
@@ -13338,9 +13436,9 @@ var init_event_hooks = __esm(() => {
|
|
|
13338
13436
|
// node_modules/.bun/@hasna+events@0.1.16/node_modules/@hasna/events/dist/index.js
|
|
13339
13437
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
13340
13438
|
import { Buffer as Buffer2 } from "buffer";
|
|
13341
|
-
import { existsSync as
|
|
13342
|
-
import { homedir as
|
|
13343
|
-
import { join as
|
|
13439
|
+
import { existsSync as existsSync7 } from "fs";
|
|
13440
|
+
import { homedir as homedir4 } from "os";
|
|
13441
|
+
import { join as join6 } from "path";
|
|
13344
13442
|
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
13345
13443
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
13346
13444
|
import { spawn } from "child_process";
|
|
@@ -13441,7 +13539,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
13441
13539
|
return channel.filters.some((filter) => eventMatchesFilter(event, filter));
|
|
13442
13540
|
}
|
|
13443
13541
|
function getEventsDataDir(override) {
|
|
13444
|
-
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] ||
|
|
13542
|
+
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join6(homedir4(), ".hasna", "events");
|
|
13445
13543
|
}
|
|
13446
13544
|
|
|
13447
13545
|
class JsonEventsStore {
|
|
@@ -13450,12 +13548,12 @@ class JsonEventsStore {
|
|
|
13450
13548
|
channelsPath;
|
|
13451
13549
|
eventsPath;
|
|
13452
13550
|
deliveriesPath;
|
|
13453
|
-
constructor(
|
|
13454
|
-
this.dataDir =
|
|
13455
|
-
this.runtime = localJsonRuntime(
|
|
13456
|
-
this.channelsPath =
|
|
13457
|
-
this.eventsPath =
|
|
13458
|
-
this.deliveriesPath =
|
|
13551
|
+
constructor(dataDir2 = getEventsDataDir()) {
|
|
13552
|
+
this.dataDir = dataDir2;
|
|
13553
|
+
this.runtime = localJsonRuntime(dataDir2);
|
|
13554
|
+
this.channelsPath = join6(dataDir2, "channels.json");
|
|
13555
|
+
this.eventsPath = join6(dataDir2, "events.json");
|
|
13556
|
+
this.deliveriesPath = join6(dataDir2, "deliveries.json");
|
|
13459
13557
|
}
|
|
13460
13558
|
async init() {
|
|
13461
13559
|
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
@@ -13572,7 +13670,7 @@ class JsonEventsStore {
|
|
|
13572
13670
|
};
|
|
13573
13671
|
}
|
|
13574
13672
|
async ensureArrayFile(path) {
|
|
13575
|
-
if (!
|
|
13673
|
+
if (!existsSync7(path)) {
|
|
13576
13674
|
await writeFile(path, `[]
|
|
13577
13675
|
`, { encoding: "utf-8", mode: 384 });
|
|
13578
13676
|
}
|
|
@@ -13602,7 +13700,7 @@ class JsonEventsStore {
|
|
|
13602
13700
|
});
|
|
13603
13701
|
}
|
|
13604
13702
|
}
|
|
13605
|
-
function localJsonRuntime(
|
|
13703
|
+
function localJsonRuntime(dataDir2 = getEventsDataDir()) {
|
|
13606
13704
|
return {
|
|
13607
13705
|
mode: "local-files",
|
|
13608
13706
|
name: "json-events-store",
|
|
@@ -13615,7 +13713,7 @@ function localJsonRuntime(dataDir = getEventsDataDir()) {
|
|
|
13615
13713
|
durable: true,
|
|
13616
13714
|
idempotency: "best-effort-local",
|
|
13617
13715
|
replayCursors: true,
|
|
13618
|
-
description: `Local JSON files in ${
|
|
13716
|
+
description: `Local JSON files in ${dataDir2}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
|
|
13619
13717
|
};
|
|
13620
13718
|
}
|
|
13621
13719
|
function encodeLocalJsonEventCursor(offset, options = {}) {
|
|
@@ -13794,7 +13892,7 @@ async function dispatchCommand(event, channel) {
|
|
|
13794
13892
|
HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
|
|
13795
13893
|
HASNA_EVENT_JSON: eventJson
|
|
13796
13894
|
};
|
|
13797
|
-
return new Promise((
|
|
13895
|
+
return new Promise((resolve8) => {
|
|
13798
13896
|
const child = spawn(channel.command.command, channel.command.args ?? [], {
|
|
13799
13897
|
cwd: channel.command.cwd,
|
|
13800
13898
|
env,
|
|
@@ -13812,7 +13910,7 @@ async function dispatchCommand(event, channel) {
|
|
|
13812
13910
|
});
|
|
13813
13911
|
child.on("error", (error) => {
|
|
13814
13912
|
clearTimeout(timeout);
|
|
13815
|
-
|
|
13913
|
+
resolve8({
|
|
13816
13914
|
attempt: 1,
|
|
13817
13915
|
status: "failed",
|
|
13818
13916
|
startedAt,
|
|
@@ -13825,7 +13923,7 @@ async function dispatchCommand(event, channel) {
|
|
|
13825
13923
|
child.on("close", (code, signal) => {
|
|
13826
13924
|
clearTimeout(timeout);
|
|
13827
13925
|
const success = code === 0;
|
|
13828
|
-
|
|
13926
|
+
resolve8({
|
|
13829
13927
|
attempt: 1,
|
|
13830
13928
|
status: success ? "success" : "failed",
|
|
13831
13929
|
startedAt,
|
|
@@ -14167,7 +14265,7 @@ function normalizeRetryPolicy(policy) {
|
|
|
14167
14265
|
};
|
|
14168
14266
|
}
|
|
14169
14267
|
var HASNA_EVENTS_DIR_ENV = "HASNA_EVENTS_DIR", HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME", LOCAL_JSON_EVENT_CURSOR_PREFIX = "local-json-v1:", DEFAULT_EVENT_PAGE_LIMIT = 100, MAX_EVENT_PAGE_LIMIT = 1000, DEFAULT_SIGNATURE_TOLERANCE_MS, EventValidationError, defaultEventTypeCatalog, APP_EVENT_V1_MAX_DATA_BYTES;
|
|
14170
|
-
var
|
|
14268
|
+
var init_dist2 = __esm(() => {
|
|
14171
14269
|
DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
14172
14270
|
EventValidationError = class EventValidationError extends Error {
|
|
14173
14271
|
eventType;
|
|
@@ -14587,7 +14685,7 @@ function emitSharedTaskEventQuiet(input) {
|
|
|
14587
14685
|
}
|
|
14588
14686
|
var SOURCE = "todos";
|
|
14589
14687
|
var init_shared_events = __esm(() => {
|
|
14590
|
-
|
|
14688
|
+
init_dist2();
|
|
14591
14689
|
init_database();
|
|
14592
14690
|
init_projects();
|
|
14593
14691
|
init_task_lists();
|
|
@@ -16449,17 +16547,17 @@ function sanitizeCreateTaskInput(input) {
|
|
|
16449
16547
|
return {
|
|
16450
16548
|
...input,
|
|
16451
16549
|
title: sanitizePreWriteText(input.title, "task.title"),
|
|
16452
|
-
description: input.description
|
|
16550
|
+
description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
|
|
16453
16551
|
tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
|
|
16454
16552
|
metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined,
|
|
16455
|
-
reason: input.reason
|
|
16553
|
+
reason: input.reason == null ? input.reason : sanitizePreWriteText(input.reason, "task.reason")
|
|
16456
16554
|
};
|
|
16457
16555
|
}
|
|
16458
16556
|
function sanitizeUpdateTaskInput(input) {
|
|
16459
16557
|
return {
|
|
16460
16558
|
...input,
|
|
16461
16559
|
title: input.title !== undefined ? sanitizePreWriteText(input.title, "task.title") : undefined,
|
|
16462
|
-
description: input.description
|
|
16560
|
+
description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
|
|
16463
16561
|
tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
|
|
16464
16562
|
metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined
|
|
16465
16563
|
};
|
|
@@ -18456,28 +18554,28 @@ var init_boards = __esm(() => {
|
|
|
18456
18554
|
|
|
18457
18555
|
// src/lib/artifact-store.ts
|
|
18458
18556
|
import { createHash as createHash6 } from "crypto";
|
|
18459
|
-
import { existsSync as
|
|
18460
|
-
import { basename, dirname as dirname4, join as
|
|
18557
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync3, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
18558
|
+
import { basename, dirname as dirname4, join as join7, resolve as resolve8 } from "path";
|
|
18461
18559
|
import { tmpdir as tmpdir2 } from "os";
|
|
18462
18560
|
function isInMemoryDb2(path) {
|
|
18463
18561
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
18464
18562
|
}
|
|
18465
18563
|
function artifactStoreRoot() {
|
|
18466
18564
|
if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
|
|
18467
|
-
return
|
|
18565
|
+
return resolve8(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
|
|
18468
18566
|
if (process.env["TODOS_ARTIFACTS_DIR"])
|
|
18469
|
-
return
|
|
18567
|
+
return resolve8(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
18470
18568
|
const dbPath = getDatabasePath();
|
|
18471
18569
|
if (isInMemoryDb2(dbPath))
|
|
18472
|
-
return
|
|
18473
|
-
return
|
|
18570
|
+
return join7(tmpdir2(), "hasna-todos-artifacts");
|
|
18571
|
+
return join7(dirname4(resolve8(dbPath)), "artifacts");
|
|
18474
18572
|
}
|
|
18475
18573
|
function artifactStorePath(relativePath) {
|
|
18476
18574
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
18477
18575
|
if (normalized.includes("..") || normalized.startsWith("/") || normalized.length === 0) {
|
|
18478
18576
|
throw new Error("Invalid artifact store path");
|
|
18479
18577
|
}
|
|
18480
|
-
return
|
|
18578
|
+
return join7(artifactStoreRoot(), normalized);
|
|
18481
18579
|
}
|
|
18482
18580
|
function sha2562(buffer) {
|
|
18483
18581
|
return createHash6("sha256").update(buffer).digest("hex");
|
|
@@ -18517,8 +18615,8 @@ function mediaTypeFor(path, textLike) {
|
|
|
18517
18615
|
return "application/octet-stream";
|
|
18518
18616
|
}
|
|
18519
18617
|
function storeArtifactContent(input) {
|
|
18520
|
-
const sourcePath =
|
|
18521
|
-
if (!
|
|
18618
|
+
const sourcePath = resolve8(input.path);
|
|
18619
|
+
if (!existsSync8(sourcePath))
|
|
18522
18620
|
return null;
|
|
18523
18621
|
const sourceStat = statSync2(sourcePath);
|
|
18524
18622
|
if (!sourceStat.isFile())
|
|
@@ -18535,9 +18633,9 @@ function storeArtifactContent(input) {
|
|
|
18535
18633
|
redactionStatus = "redacted";
|
|
18536
18634
|
}
|
|
18537
18635
|
const storedSha = sha2562(storedBuffer);
|
|
18538
|
-
const relativePath =
|
|
18636
|
+
const relativePath = join7("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
|
|
18539
18637
|
const destination = artifactStorePath(relativePath);
|
|
18540
|
-
if (!
|
|
18638
|
+
if (!existsSync8(destination)) {
|
|
18541
18639
|
mkdirSync4(dirname4(destination), { recursive: true });
|
|
18542
18640
|
writeFileSync2(destination, storedBuffer);
|
|
18543
18641
|
}
|
|
@@ -18597,7 +18695,7 @@ function verifyStoredArtifact(input) {
|
|
|
18597
18695
|
};
|
|
18598
18696
|
}
|
|
18599
18697
|
const storedPath = artifactStorePath(store.relative_path);
|
|
18600
|
-
if (!
|
|
18698
|
+
if (!existsSync8(storedPath)) {
|
|
18601
18699
|
return {
|
|
18602
18700
|
id: input.id,
|
|
18603
18701
|
path: input.path,
|
|
@@ -31161,8 +31259,8 @@ var exports_doctor = {};
|
|
|
31161
31259
|
__export(exports_doctor, {
|
|
31162
31260
|
runTodosDoctor: () => runTodosDoctor
|
|
31163
31261
|
});
|
|
31164
|
-
import { chmodSync, copyFileSync, existsSync as
|
|
31165
|
-
import { basename as basename2, dirname as dirname5, join as
|
|
31262
|
+
import { chmodSync, copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync5, statSync as statSync3 } from "fs";
|
|
31263
|
+
import { basename as basename2, dirname as dirname5, join as join8 } from "path";
|
|
31166
31264
|
function tableExists2(db, table) {
|
|
31167
31265
|
return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
|
|
31168
31266
|
}
|
|
@@ -31256,7 +31354,7 @@ function findMissingProjectRoots(db) {
|
|
|
31256
31354
|
continue;
|
|
31257
31355
|
if (!row.path.startsWith("/"))
|
|
31258
31356
|
continue;
|
|
31259
|
-
if (!
|
|
31357
|
+
if (!existsSync9(row.path))
|
|
31260
31358
|
missing++;
|
|
31261
31359
|
}
|
|
31262
31360
|
return missing;
|
|
@@ -31316,16 +31414,16 @@ function databasePermissionsAreUnsafe(dbPath) {
|
|
|
31316
31414
|
function createBackup(dbPath) {
|
|
31317
31415
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
31318
31416
|
return;
|
|
31319
|
-
if (!
|
|
31417
|
+
if (!existsSync9(dbPath))
|
|
31320
31418
|
return;
|
|
31321
31419
|
const stamp = now().replace(/[:.]/g, "-");
|
|
31322
|
-
const backupDir =
|
|
31420
|
+
const backupDir = join8(dirname5(dbPath), `${basename2(dbPath)}.backup-${stamp}`);
|
|
31323
31421
|
const files = [];
|
|
31324
31422
|
mkdirSync5(backupDir, { recursive: true });
|
|
31325
31423
|
for (const source of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
31326
|
-
if (!
|
|
31424
|
+
if (!existsSync9(source))
|
|
31327
31425
|
continue;
|
|
31328
|
-
const target =
|
|
31426
|
+
const target = join8(backupDir, basename2(source));
|
|
31329
31427
|
copyFileSync(source, target);
|
|
31330
31428
|
files.push(target);
|
|
31331
31429
|
}
|
|
@@ -31584,7 +31682,7 @@ var init_doctor = __esm(() => {
|
|
|
31584
31682
|
});
|
|
31585
31683
|
|
|
31586
31684
|
// src/server/routes.ts
|
|
31587
|
-
import { join as
|
|
31685
|
+
import { join as join9, resolve as resolve9, sep as sep2 } from "path";
|
|
31588
31686
|
function parseFieldsParam(url) {
|
|
31589
31687
|
const fieldsParam = url.searchParams.get("fields");
|
|
31590
31688
|
return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
|
|
@@ -32421,9 +32519,9 @@ function handleStaticFiles(path, method, ctx, json5, serveStaticFile2) {
|
|
|
32421
32519
|
if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
|
|
32422
32520
|
return null;
|
|
32423
32521
|
if (path !== "/") {
|
|
32424
|
-
const filePath =
|
|
32425
|
-
const resolvedFile =
|
|
32426
|
-
const resolvedBase =
|
|
32522
|
+
const filePath = join9(ctx.dashboardDir, path);
|
|
32523
|
+
const resolvedFile = resolve9(filePath);
|
|
32524
|
+
const resolvedBase = resolve9(ctx.dashboardDir);
|
|
32427
32525
|
if (!resolvedFile.startsWith(resolvedBase + sep2) && resolvedFile !== resolvedBase) {
|
|
32428
32526
|
return json5({ error: "Forbidden" }, 403);
|
|
32429
32527
|
}
|
|
@@ -32431,7 +32529,7 @@ function handleStaticFiles(path, method, ctx, json5, serveStaticFile2) {
|
|
|
32431
32529
|
if (res2)
|
|
32432
32530
|
return res2;
|
|
32433
32531
|
}
|
|
32434
|
-
const indexPath =
|
|
32532
|
+
const indexPath = join9(ctx.dashboardDir, "index.html");
|
|
32435
32533
|
const res = serveStaticFile2(indexPath);
|
|
32436
32534
|
if (res)
|
|
32437
32535
|
return res;
|
|
@@ -32538,7 +32636,7 @@ class TodosShadowOutbox {
|
|
|
32538
32636
|
const remaining = deadline - Date.now();
|
|
32539
32637
|
if (remaining <= 0)
|
|
32540
32638
|
break;
|
|
32541
|
-
await new Promise((
|
|
32639
|
+
await new Promise((resolve10) => setTimeout(resolve10, Math.min(200, remaining)));
|
|
32542
32640
|
}
|
|
32543
32641
|
}
|
|
32544
32642
|
return this.getStats();
|
|
@@ -44573,9 +44671,9 @@ data:
|
|
|
44573
44671
|
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
44574
44672
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
44575
44673
|
if (this._enableJsonResponse) {
|
|
44576
|
-
return new Promise((
|
|
44674
|
+
return new Promise((resolve10) => {
|
|
44577
44675
|
this._streamMapping.set(streamId, {
|
|
44578
|
-
resolveJson:
|
|
44676
|
+
resolveJson: resolve10,
|
|
44579
44677
|
cleanup: () => {
|
|
44580
44678
|
this._streamMapping.delete(streamId);
|
|
44581
44679
|
}
|
|
@@ -47002,7 +47100,7 @@ class Protocol {
|
|
|
47002
47100
|
return;
|
|
47003
47101
|
}
|
|
47004
47102
|
const pollInterval = task3.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
47005
|
-
await new Promise((
|
|
47103
|
+
await new Promise((resolve10) => setTimeout(resolve10, pollInterval));
|
|
47006
47104
|
options?.signal?.throwIfAborted();
|
|
47007
47105
|
}
|
|
47008
47106
|
} catch (error3) {
|
|
@@ -47014,7 +47112,7 @@ class Protocol {
|
|
|
47014
47112
|
}
|
|
47015
47113
|
request(request, resultSchema, options) {
|
|
47016
47114
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task: task2, relatedTask } = options ?? {};
|
|
47017
|
-
return new Promise((
|
|
47115
|
+
return new Promise((resolve10, reject) => {
|
|
47018
47116
|
const earlyReject = (error3) => {
|
|
47019
47117
|
reject(error3);
|
|
47020
47118
|
};
|
|
@@ -47092,7 +47190,7 @@ class Protocol {
|
|
|
47092
47190
|
if (!parseResult.success) {
|
|
47093
47191
|
reject(parseResult.error);
|
|
47094
47192
|
} else {
|
|
47095
|
-
|
|
47193
|
+
resolve10(parseResult.data);
|
|
47096
47194
|
}
|
|
47097
47195
|
} catch (error3) {
|
|
47098
47196
|
reject(error3);
|
|
@@ -47283,12 +47381,12 @@ class Protocol {
|
|
|
47283
47381
|
interval = task2.pollInterval;
|
|
47284
47382
|
}
|
|
47285
47383
|
} catch {}
|
|
47286
|
-
return new Promise((
|
|
47384
|
+
return new Promise((resolve10, reject) => {
|
|
47287
47385
|
if (signal.aborted) {
|
|
47288
47386
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
47289
47387
|
return;
|
|
47290
47388
|
}
|
|
47291
|
-
const timeoutId = setTimeout(
|
|
47389
|
+
const timeoutId = setTimeout(resolve10, interval);
|
|
47292
47390
|
signal.addEventListener("abort", () => {
|
|
47293
47391
|
clearTimeout(timeoutId);
|
|
47294
47392
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -50273,7 +50371,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
50273
50371
|
const schOrFunc = root.refs[ref];
|
|
50274
50372
|
if (schOrFunc)
|
|
50275
50373
|
return schOrFunc;
|
|
50276
|
-
let _sch =
|
|
50374
|
+
let _sch = resolve10.call(this, root, ref);
|
|
50277
50375
|
if (_sch === undefined) {
|
|
50278
50376
|
const schema2 = (_a = root.localRefs) === null || _a === undefined ? undefined : _a[ref];
|
|
50279
50377
|
const { schemaId } = this.opts;
|
|
@@ -50300,7 +50398,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
50300
50398
|
function sameSchemaEnv(s1, s2) {
|
|
50301
50399
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
50302
50400
|
}
|
|
50303
|
-
function
|
|
50401
|
+
function resolve10(root, ref) {
|
|
50304
50402
|
let sch;
|
|
50305
50403
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
50306
50404
|
ref = sch;
|
|
@@ -50886,7 +50984,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
50886
50984
|
}
|
|
50887
50985
|
return uri;
|
|
50888
50986
|
}
|
|
50889
|
-
function
|
|
50987
|
+
function resolve10(baseURI, relativeURI, options) {
|
|
50890
50988
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
50891
50989
|
const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
50892
50990
|
schemelessOptions.skipEscape = true;
|
|
@@ -51145,7 +51243,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
51145
51243
|
var fastUri = {
|
|
51146
51244
|
SCHEMES,
|
|
51147
51245
|
normalize: normalize2,
|
|
51148
|
-
resolve:
|
|
51246
|
+
resolve: resolve10,
|
|
51149
51247
|
resolveComponent,
|
|
51150
51248
|
equal,
|
|
51151
51249
|
serialize,
|
|
@@ -54716,7 +54814,7 @@ class McpServer {
|
|
|
54716
54814
|
let task2 = createTaskResult.task;
|
|
54717
54815
|
const pollInterval = task2.pollInterval ?? 5000;
|
|
54718
54816
|
while (task2.status !== "completed" && task2.status !== "failed" && task2.status !== "cancelled") {
|
|
54719
|
-
await new Promise((
|
|
54817
|
+
await new Promise((resolve10) => setTimeout(resolve10, pollInterval));
|
|
54720
54818
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
54721
54819
|
if (!updatedTask) {
|
|
54722
54820
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -55361,12 +55459,12 @@ class StdioServerTransport {
|
|
|
55361
55459
|
this.onclose?.();
|
|
55362
55460
|
}
|
|
55363
55461
|
send(message) {
|
|
55364
|
-
return new Promise((
|
|
55462
|
+
return new Promise((resolve10) => {
|
|
55365
55463
|
const json7 = serializeMessage(message);
|
|
55366
55464
|
if (this._stdout.write(json7)) {
|
|
55367
|
-
|
|
55465
|
+
resolve10();
|
|
55368
55466
|
} else {
|
|
55369
|
-
this._stdout.once("drain",
|
|
55467
|
+
this._stdout.once("drain", resolve10);
|
|
55370
55468
|
}
|
|
55371
55469
|
});
|
|
55372
55470
|
}
|
|
@@ -56515,10 +56613,10 @@ var init_token_utils = __esm(() => {
|
|
|
56515
56613
|
|
|
56516
56614
|
// src/lib/assignee-validation.ts
|
|
56517
56615
|
import { readFileSync as readFileSync4 } from "fs";
|
|
56518
|
-
import { homedir as
|
|
56519
|
-
import { join as
|
|
56616
|
+
import { homedir as homedir5 } from "os";
|
|
56617
|
+
import { join as join10 } from "path";
|
|
56520
56618
|
function defaultSeatRosterPath() {
|
|
56521
|
-
return process.env["TODOS_SEAT_ROSTER_PATH"] ||
|
|
56619
|
+
return process.env["TODOS_SEAT_ROSTER_PATH"] || join10(homedir5(), ".hasna", "identities", "hasna-seats.roster.json");
|
|
56522
56620
|
}
|
|
56523
56621
|
function loadSeatSlugs(path = defaultSeatRosterPath()) {
|
|
56524
56622
|
try {
|
|
@@ -56608,10 +56706,11 @@ var init_assignee_context = __esm(() => {
|
|
|
56608
56706
|
init_assignee_validation();
|
|
56609
56707
|
});
|
|
56610
56708
|
|
|
56611
|
-
// node_modules/.bun/@hasna+contracts@0.14.
|
|
56709
|
+
// node_modules/.bun/@hasna+contracts@0.14.2+a5fb9613d5af3331/node_modules/@hasna/contracts/dist/client/storage.js
|
|
56612
56710
|
import { isIP } from "net";
|
|
56613
56711
|
import { readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
56614
|
-
import {
|
|
56712
|
+
import { createRequire } from "module";
|
|
56713
|
+
import { join as join11 } from "path";
|
|
56615
56714
|
function envToken(name) {
|
|
56616
56715
|
return name.toUpperCase().replace(/-/g, "_");
|
|
56617
56716
|
}
|
|
@@ -56625,24 +56724,48 @@ function clientTransportEnvKeys(name) {
|
|
|
56625
56724
|
function credentialOverrideEnvKey(name) {
|
|
56626
56725
|
return `HASNA_${envToken(name)}_API_KEY_OVERRIDE`;
|
|
56627
56726
|
}
|
|
56727
|
+
function credentialPointerEnvKey(name) {
|
|
56728
|
+
return `HASNA_${envToken(name)}_API_KEY_REF`;
|
|
56729
|
+
}
|
|
56628
56730
|
function homeDir(env) {
|
|
56629
56731
|
const home = env.HOME?.trim();
|
|
56630
56732
|
return home ? home : null;
|
|
56631
56733
|
}
|
|
56632
|
-
function
|
|
56633
|
-
return profileDiskSources(name, env, null);
|
|
56634
|
-
}
|
|
56635
|
-
function profileDiskSources(name, env, profile) {
|
|
56734
|
+
function credentialDiskSourceList(name, env, profile = null) {
|
|
56636
56735
|
const home = homeDir(env);
|
|
56637
56736
|
if (!home || !SAFE_APP_SLUG.test(name))
|
|
56638
56737
|
return [];
|
|
56639
56738
|
const stem = profile ? `${name}.${profile}` : name;
|
|
56640
56739
|
const configStem = profile ? `${name}-${profile}` : name;
|
|
56641
56740
|
return [
|
|
56642
|
-
|
|
56643
|
-
|
|
56741
|
+
{
|
|
56742
|
+
path: join11(home, HASNA_STATE_DIR, FLEET_CREDENTIAL_DIR, `${stem}.env`),
|
|
56743
|
+
tier: "fleet-env",
|
|
56744
|
+
deprecated: false
|
|
56745
|
+
},
|
|
56746
|
+
{
|
|
56747
|
+
path: join11(home, HASNA_STATE_DIR, LEGACY_CLOUD_DIR, `${stem}.env`),
|
|
56748
|
+
tier: "legacy-cloud",
|
|
56749
|
+
deprecated: true
|
|
56750
|
+
},
|
|
56751
|
+
{
|
|
56752
|
+
path: join11(home, CONFIG_DIR, CONFIG_NAMESPACE, `${configStem}.env`),
|
|
56753
|
+
tier: "config",
|
|
56754
|
+
deprecated: false
|
|
56755
|
+
},
|
|
56756
|
+
{
|
|
56757
|
+
path: join11(home, CONFIG_DIR, CONFIG_NAMESPACE, `${configStem}-cloud.env`),
|
|
56758
|
+
tier: "config-legacy",
|
|
56759
|
+
deprecated: true
|
|
56760
|
+
}
|
|
56644
56761
|
];
|
|
56645
56762
|
}
|
|
56763
|
+
function credentialDiskSources(name, env) {
|
|
56764
|
+
return credentialDiskSourceList(name, env, null).map((s) => s.path);
|
|
56765
|
+
}
|
|
56766
|
+
function profileDiskSources(name, env, profile) {
|
|
56767
|
+
return credentialDiskSourceList(name, env, profile).map((s) => s.path);
|
|
56768
|
+
}
|
|
56646
56769
|
function parseEnvFile(text) {
|
|
56647
56770
|
const values = new Map;
|
|
56648
56771
|
for (const rawLine of text.split(/\r?\n/)) {
|
|
@@ -56709,6 +56832,9 @@ function appConfigDiskValue(name, env, keys) {
|
|
|
56709
56832
|
return null;
|
|
56710
56833
|
}
|
|
56711
56834
|
function assertUsableCredential(appName, source, value) {
|
|
56835
|
+
if (VAULT_POINTER_SHAPE.test(value)) {
|
|
56836
|
+
throw new CredentialResolutionError(appName, `The credential from ${source} looks like a secrets-vault pointer (a path-shaped reference like ` + `'namespace/app/live/api_key'). A vault path is NEVER accepted as a literal API key. ` + `Use ${credentialPointerEnvKey(appName)} to resolve the key through the vault, or provide the actual key value.`, [source]);
|
|
56837
|
+
}
|
|
56712
56838
|
if (!ILLEGAL_IN_HEADER_VALUE.test(value))
|
|
56713
56839
|
return;
|
|
56714
56840
|
throw new CredentialResolutionError(appName, `The credential from ${source} contains characters that cannot be sent in an HTTP header ` + `(a control character or non-ASCII byte). A file written with CR-only line endings is the usual ` + `cause. Rewrite that credential file with one LF-terminated KEY=value line. ` + `The value is not shown here, and is deliberately never logged.`, [source]);
|
|
@@ -56730,6 +56856,14 @@ function sealCredential(fields) {
|
|
|
56730
56856
|
writable: false,
|
|
56731
56857
|
configurable: false
|
|
56732
56858
|
});
|
|
56859
|
+
if (fields.pointerVaultKey !== undefined) {
|
|
56860
|
+
Object.defineProperty(sealed, "pointerVaultKey", {
|
|
56861
|
+
value: fields.pointerVaultKey,
|
|
56862
|
+
enumerable: false,
|
|
56863
|
+
writable: false,
|
|
56864
|
+
configurable: false
|
|
56865
|
+
});
|
|
56866
|
+
}
|
|
56733
56867
|
Object.defineProperty(sealed, INSPECT_CUSTOM, {
|
|
56734
56868
|
value: () => ({ ...visible, apiKey: "[redacted]" }),
|
|
56735
56869
|
enumerable: false,
|
|
@@ -56781,7 +56915,8 @@ function validateAndSealResolvedCredential(appName, credential) {
|
|
|
56781
56915
|
deliberate: credential.deliberate,
|
|
56782
56916
|
deprecated: credential.deprecated,
|
|
56783
56917
|
diskCandidates: credential.diskCandidates,
|
|
56784
|
-
warning: credential.warning
|
|
56918
|
+
warning: credential.warning,
|
|
56919
|
+
...credential.pointerVaultKey !== undefined ? { pointerVaultKey: credential.pointerVaultKey } : {}
|
|
56785
56920
|
});
|
|
56786
56921
|
}
|
|
56787
56922
|
function firstEnvValue(env, keys) {
|
|
@@ -56841,6 +56976,27 @@ function resolveCredential(name, env, options = {}) {
|
|
|
56841
56976
|
warning: null
|
|
56842
56977
|
});
|
|
56843
56978
|
}
|
|
56979
|
+
const pointerKeyName = credentialPointerEnvKey(name);
|
|
56980
|
+
const pointerRaw = env[pointerKeyName];
|
|
56981
|
+
if (pointerRaw !== undefined) {
|
|
56982
|
+
const pointer = pointerRaw.trim();
|
|
56983
|
+
if (!pointer) {
|
|
56984
|
+
throw new CredentialResolutionError(name, `${pointerKeyName} is set but empty. It is a deliberate vault pointer, so it is not resolved around: ` + `either give it a vault item key or unset it to fall back to the credential on disk.`, [pointerKeyName]);
|
|
56985
|
+
}
|
|
56986
|
+
if (!VAULT_POINTER_SHAPE.test(pointer)) {
|
|
56987
|
+
throw new CredentialResolutionError(name, `${pointerKeyName} must name a vault ITEM KEY (a path-shaped reference like ` + `'namespace/app/live/api_key'), not a credential value. A pointer that carries a literal is refused.`, [pointerKeyName]);
|
|
56988
|
+
}
|
|
56989
|
+
return sealCredential({
|
|
56990
|
+
apiKey: "",
|
|
56991
|
+
pointerVaultKey: pointer,
|
|
56992
|
+
tier: "pointer",
|
|
56993
|
+
source: pointerKeyName,
|
|
56994
|
+
deliberate: true,
|
|
56995
|
+
deprecated: false,
|
|
56996
|
+
diskCandidates: diskPaths,
|
|
56997
|
+
warning: null
|
|
56998
|
+
});
|
|
56999
|
+
}
|
|
56844
57000
|
const profile = options.profile?.trim() || env[CREDENTIAL_PROFILE_ENV_KEY]?.trim();
|
|
56845
57001
|
if (profile) {
|
|
56846
57002
|
const profileSource = options.profile?.trim() ? "explicit profile argument" : CREDENTIAL_PROFILE_ENV_KEY;
|
|
@@ -56865,26 +57021,42 @@ function resolveCredential(name, env, options = {}) {
|
|
|
56865
57021
|
}
|
|
56866
57022
|
throw new CredentialResolutionError(name, `Profile '${profile}' (from ${profileSource}) has no ${apiKeyKeys[0]} for '${name}'. ` + `Looked in: ${paths.join(", ") || "<no HOME in this environment>"}. ` + `A profile names WHICH identity to use, so it is never resolved around \u2014 ` + `create the profile's credential file or unset ${CREDENTIAL_PROFILE_ENV_KEY}.`, paths);
|
|
56867
57023
|
}
|
|
56868
|
-
const
|
|
57024
|
+
const diskSourceList = credentialDiskSourceList(name, env, null);
|
|
57025
|
+
const diskHits = diskSourceList.map((src) => ({ src, value: readCredentialFile(src.path, apiKeyKeys) })).filter((hit) => hit.value !== null);
|
|
56869
57026
|
if (diskHits.length > 0) {
|
|
56870
57027
|
const winner = diskHits[0];
|
|
56871
|
-
assertUsableCredential(name, winner.path, winner.value);
|
|
57028
|
+
assertUsableCredential(name, winner.src.path, winner.value);
|
|
56872
57029
|
const divergentSources = [
|
|
56873
|
-
...diskHits.slice(1).filter((hit) => hit.value !== winner.value).map((hit) => hit.path),
|
|
57030
|
+
...diskHits.slice(1).filter((hit) => hit.value !== winner.value).map((hit) => hit.src.path),
|
|
56874
57031
|
...(() => {
|
|
56875
57032
|
const legacyHit = firstEnvValue(env, apiKeyKeys);
|
|
56876
57033
|
return legacyHit && legacyHit.value !== winner.value ? [legacyHit.key] : [];
|
|
56877
57034
|
})()
|
|
56878
57035
|
];
|
|
56879
|
-
const warning = divergentSources.length > 0 ? `Credential sources disagree for '${name}': ${winner.path} and ` + `${divergentSources.join(", ")} hold different keys. ${winner.path} wins, because a file on ` + `disk is re-read on every call while an environment variable is a snapshot. Reconcile them \u2014 ` + `a rotation that updated only one leaves the other to fail 401 wherever it is loaded first.` : null;
|
|
57036
|
+
const warning = divergentSources.length > 0 ? `Credential sources disagree for '${name}': ${winner.src.path} and ` + `${divergentSources.join(", ")} hold different keys. ${winner.src.path} wins, because a file on ` + `disk is re-read on every call while an environment variable is a snapshot. Reconcile them \u2014 ` + `a rotation that updated only one leaves the other to fail 401 wherever it is loaded first.` : null;
|
|
57037
|
+
let deprecated = winner.src.deprecated;
|
|
57038
|
+
let finalWarning = warning;
|
|
57039
|
+
if (winner.src.deprecated) {
|
|
57040
|
+
deprecated = true;
|
|
57041
|
+
const sink = options.onDeprecation ?? defaultDeprecationSink;
|
|
57042
|
+
const notified = deprecationNotified();
|
|
57043
|
+
const noticeKey = `${name}:${winner.src.path}`;
|
|
57044
|
+
if (!notified.has(noticeKey)) {
|
|
57045
|
+
notified.add(noticeKey);
|
|
57046
|
+
const target = diskSourceList[0]?.path ?? "<none>";
|
|
57047
|
+
const message = `[${name}] DEPRECATED: the API key came from ${winner.src.path} \u2014 a legacy credential location. ` + `The primary location is ${target} (~/.hasna/fleet-env/<app>.env). The legacy 'cloud' tiers are ` + `removed after ${LEGACY_CLOUD_REMOVAL_DEADLINE}. Migrate the key to the primary location.`;
|
|
57048
|
+
sink(message);
|
|
57049
|
+
}
|
|
57050
|
+
finalWarning = [warning, `Legacy credential source: ${winner.src.path}. Removed after ${LEGACY_CLOUD_REMOVAL_DEADLINE}.`].filter(Boolean).join(" ") || null;
|
|
57051
|
+
}
|
|
56880
57052
|
return sealCredential({
|
|
56881
57053
|
apiKey: winner.value,
|
|
56882
|
-
tier:
|
|
56883
|
-
source: winner.path,
|
|
57054
|
+
tier: winner.src.tier,
|
|
57055
|
+
source: winner.src.path,
|
|
56884
57056
|
deliberate: false,
|
|
56885
|
-
deprecated
|
|
57057
|
+
deprecated,
|
|
56886
57058
|
diskCandidates: diskPaths,
|
|
56887
|
-
warning
|
|
57059
|
+
warning: finalWarning
|
|
56888
57060
|
});
|
|
56889
57061
|
}
|
|
56890
57062
|
const legacy = firstEnvValue(env, apiKeyKeys);
|
|
@@ -56910,6 +57082,45 @@ function resolveCredential(name, env, options = {}) {
|
|
|
56910
57082
|
}
|
|
56911
57083
|
return null;
|
|
56912
57084
|
}
|
|
57085
|
+
async function completePointerCredential(name, pointerResolution, env = process.env) {
|
|
57086
|
+
const vaultKey = pointerResolution.pointerVaultKey;
|
|
57087
|
+
const pointerEnvKey = pointerResolution.source;
|
|
57088
|
+
if (!vaultKey) {
|
|
57089
|
+
throw new CredentialResolutionError(name, `Pointer resolution from ${pointerEnvKey} carries no vault item key; this is a defect in the resolver.`, [pointerEnvKey]);
|
|
57090
|
+
}
|
|
57091
|
+
let secretsSdk;
|
|
57092
|
+
try {
|
|
57093
|
+
secretsSdk = requireSecretsSdk(SECRETS_PACKAGE_SPECIFIER);
|
|
57094
|
+
} catch {
|
|
57095
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the secrets SDK (@hasna/secrets) is not installed ` + `in this process. A vault pointer is TERMINAL: install @hasna/secrets to resolve it, or unset ${pointerEnvKey}.`, [pointerEnvKey]);
|
|
57096
|
+
}
|
|
57097
|
+
let client;
|
|
57098
|
+
try {
|
|
57099
|
+
client = secretsSdk.createSecretsClientFromEnv(env);
|
|
57100
|
+
} catch {
|
|
57101
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the secrets client could not be configured from this ` + `environment (the secrets service URL and key env are missing or invalid). A vault pointer is TERMINAL and ` + `never falls through to a literal or disk credential.`, [pointerEnvKey]);
|
|
57102
|
+
}
|
|
57103
|
+
let secret;
|
|
57104
|
+
try {
|
|
57105
|
+
secret = await client.getSecret({ key: vaultKey });
|
|
57106
|
+
} catch {
|
|
57107
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the vault could not be reached or the item is ` + `unavailable. A vault pointer is TERMINAL and never falls through to a literal or disk credential.`, [pointerEnvKey]);
|
|
57108
|
+
}
|
|
57109
|
+
const value = secret.value;
|
|
57110
|
+
if (!value) {
|
|
57111
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} resolved vault item '${vaultKey}', but it holds no value. A vault pointer is TERMINAL.`, [pointerEnvKey]);
|
|
57112
|
+
}
|
|
57113
|
+
assertUsableCredential(name, `${pointerEnvKey} -> vault:${vaultKey}`, value);
|
|
57114
|
+
return sealCredential({
|
|
57115
|
+
apiKey: value,
|
|
57116
|
+
tier: "pointer",
|
|
57117
|
+
source: `${pointerEnvKey} -> vault:${vaultKey}`,
|
|
57118
|
+
deliberate: true,
|
|
57119
|
+
deprecated: false,
|
|
57120
|
+
diskCandidates: pointerResolution.diskCandidates,
|
|
57121
|
+
warning: null
|
|
57122
|
+
});
|
|
57123
|
+
}
|
|
56913
57124
|
function isValidDnsDomain(value) {
|
|
56914
57125
|
if (value.length === 0 || value.length > 253 || ASCII_CONTROL_PATTERN.test(value) || /[^\x00-\x7f]/.test(value)) {
|
|
56915
57126
|
return false;
|
|
@@ -57133,6 +57344,13 @@ function currentCredential(name, apiKey) {
|
|
|
57133
57344
|
}
|
|
57134
57345
|
return explicitCredential(name, apiKey);
|
|
57135
57346
|
}
|
|
57347
|
+
async function resolveRequestCredential(name, apiKey, env = process.env) {
|
|
57348
|
+
const resolved = currentCredential(name, apiKey);
|
|
57349
|
+
if (resolved.tier === "pointer") {
|
|
57350
|
+
return completePointerCredential(name, resolved, env);
|
|
57351
|
+
}
|
|
57352
|
+
return resolved;
|
|
57353
|
+
}
|
|
57136
57354
|
function authFailureGuidance(credential) {
|
|
57137
57355
|
const origin = `The API key for this request came from ${credential.source}`;
|
|
57138
57356
|
if (credential.deliberate) {
|
|
@@ -57278,7 +57496,7 @@ function createHasnaHttpTransport(options) {
|
|
|
57278
57496
|
const retry = resolveRetry(opts.retry);
|
|
57279
57497
|
const methodRetryable = IDEMPOTENT_METHODS.has(upper) || Boolean(opts.idempotencyKey);
|
|
57280
57498
|
const maxAttempts = retry && methodRetryable ? retry.retries + 1 : 1;
|
|
57281
|
-
const credential =
|
|
57499
|
+
const credential = await resolveRequestCredential(options.name, options.apiKey);
|
|
57282
57500
|
let last = null;
|
|
57283
57501
|
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
57284
57502
|
const result = await once(upper, rel, url, body2, opts, credential);
|
|
@@ -57441,7 +57659,7 @@ function resolveStorageClient(name, env = process.env, overrides) {
|
|
|
57441
57659
|
}
|
|
57442
57660
|
return { transport: "sqlite", client: null };
|
|
57443
57661
|
}
|
|
57444
|
-
var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE", CredentialResolutionError, HASNA_STATE_DIR = ".hasna", FLEET_CREDENTIAL_DIR = "cloud", CONFIG_DIR = ".config", CONFIG_NAMESPACE = "hasna", MAX_CREDENTIAL_FILE_BYTES, SAFE_APP_SLUG, SAFE_PROFILE, ILLEGAL_IN_HEADER_VALUE, CREDENTIAL_SHAPED_KEY, INSPECT_CUSTOM, CREDENTIAL_SEAL, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE = "caller-supplied CredentialProvider", DEPRECATION_REGISTRY, ASCII_CONTROL_PATTERN, DNS_LABEL_PATTERN, HasnaHttpError, DEFAULT_RETRY_STATUSES, IDEMPOTENT_METHODS, AUTHORITY_OVERRIDE_HEADERS, defaultSleep = (ms) => new Promise((
|
|
57662
|
+
var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE", CredentialResolutionError, HASNA_STATE_DIR = ".hasna", FLEET_CREDENTIAL_DIR = "fleet-env", LEGACY_CLOUD_DIR = "cloud", CONFIG_DIR = ".config", CONFIG_NAMESPACE = "hasna", LEGACY_CLOUD_REMOVAL_DEADLINE = "2026-10-01", MAX_CREDENTIAL_FILE_BYTES, SAFE_APP_SLUG, SAFE_PROFILE, ILLEGAL_IN_HEADER_VALUE, VAULT_POINTER_SHAPE, CREDENTIAL_SHAPED_KEY, INSPECT_CUSTOM, CREDENTIAL_SEAL, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE = "caller-supplied CredentialProvider", DEPRECATION_REGISTRY, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk, ASCII_CONTROL_PATTERN, DNS_LABEL_PATTERN, HasnaHttpError, DEFAULT_RETRY_STATUSES, IDEMPOTENT_METHODS, AUTHORITY_OVERRIDE_HEADERS, defaultSleep = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
57445
57663
|
var init_storage = __esm(() => {
|
|
57446
57664
|
CredentialResolutionError = class CredentialResolutionError extends Error {
|
|
57447
57665
|
appName;
|
|
@@ -57457,10 +57675,13 @@ var init_storage = __esm(() => {
|
|
|
57457
57675
|
SAFE_APP_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
57458
57676
|
SAFE_PROFILE = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
|
|
57459
57677
|
ILLEGAL_IN_HEADER_VALUE = /[^\t\x20-\x7e]/;
|
|
57678
|
+
VAULT_POINTER_SHAPE = /^[a-z0-9][a-z0-9-]*(?:\/[a-z0-9][a-z0-9-_.]*){2,}$/;
|
|
57460
57679
|
CREDENTIAL_SHAPED_KEY = /(?:^|_)(?:API_KEY|KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)(?:_|$)/;
|
|
57461
57680
|
INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
|
|
57462
57681
|
CREDENTIAL_SEAL = Symbol.for("hasna:contracts:sealedCredential");
|
|
57463
57682
|
DEPRECATION_REGISTRY = Symbol.for("hasna:contracts:credentialDeprecationNotices");
|
|
57683
|
+
SECRETS_PACKAGE_SPECIFIER = "@hasna/" + "secrets";
|
|
57684
|
+
requireSecretsSdk = createRequire(import.meta.url);
|
|
57464
57685
|
ASCII_CONTROL_PATTERN = /[\u0000-\u001f\u007f]/;
|
|
57465
57686
|
DNS_LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
57466
57687
|
HasnaHttpError = class HasnaHttpError extends Error {
|
|
@@ -58339,7 +58560,7 @@ var init_page_validation = __esm(() => {
|
|
|
58339
58560
|
|
|
58340
58561
|
// src/cli/cloud-router.ts
|
|
58341
58562
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
58342
|
-
import { resolve as
|
|
58563
|
+
import { resolve as resolvePath2 } from "path";
|
|
58343
58564
|
function emitTodosLocalFallbackNotice(env) {
|
|
58344
58565
|
if (todosLocalFallbackNoticeEmitted)
|
|
58345
58566
|
return;
|
|
@@ -58399,15 +58620,23 @@ function normalizeRemoteAuthorityUrl(value) {
|
|
|
58399
58620
|
if (url.search || url.hash) {
|
|
58400
58621
|
throw new Error("REMOTE_API_URL_INVALID: HASNA_TODOS_API_URL must not contain a query or fragment; local SQLite fallback is disabled");
|
|
58401
58622
|
}
|
|
58402
|
-
|
|
58403
|
-
|
|
58623
|
+
const path = url.pathname.replace(/\/+$/, "");
|
|
58624
|
+
const segments = path.split("/").filter(Boolean);
|
|
58625
|
+
const reservedGatewaySegments = new Set(["api", "v1"]);
|
|
58626
|
+
const isRoot = path === "";
|
|
58627
|
+
const isV1Root = segments.length === 1 && segments[0] === "v1";
|
|
58628
|
+
const isAppRoot = segments.length === 1 && !reservedGatewaySegments.has(segments[0].toLowerCase());
|
|
58629
|
+
const isAppV1Root = segments.length === 2 && segments[1] === "v1" && !reservedGatewaySegments.has(segments[0].toLowerCase());
|
|
58630
|
+
if (!isRoot && !isV1Root && !isAppRoot && !isAppV1Root) {
|
|
58631
|
+
throw new Error("REMOTE_API_URL_INVALID: HASNA_TODOS_API_URL must be an authority root, /v1, or <app>[/v1], not /api/v1 or another path; " + "local SQLite fallback is disabled");
|
|
58404
58632
|
}
|
|
58405
58633
|
const hostname3 = url.hostname.toLowerCase();
|
|
58406
58634
|
const loopback = hostname3 === "localhost" || hostname3 === "::1" || hostname3 === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(hostname3);
|
|
58407
58635
|
if (url.protocol === "http:" && !loopback) {
|
|
58408
58636
|
throw new Error("REMOTE_API_URL_INVALID: plaintext HTTP is allowed only for loopback Todos authorities; local SQLite fallback is disabled");
|
|
58409
58637
|
}
|
|
58410
|
-
|
|
58638
|
+
const rootPath = isV1Root || isAppV1Root ? path.slice(0, -"/v1".length) : path;
|
|
58639
|
+
return rootPath ? `${url.origin}${rootPath}` : url.origin;
|
|
58411
58640
|
}
|
|
58412
58641
|
function getTodosRemoteAuthorityConfigStatus(env = process.env) {
|
|
58413
58642
|
let resolution;
|
|
@@ -58905,11 +59134,11 @@ function resolveCloudProjectRef(projects, ref) {
|
|
|
58905
59134
|
const input = ref.trim();
|
|
58906
59135
|
const normalizedRef = input.toLowerCase();
|
|
58907
59136
|
const pathLike = input.startsWith(".") || input.includes("/") || input.includes("\\");
|
|
58908
|
-
const normalizedPath = pathLike ?
|
|
59137
|
+
const normalizedPath = pathLike ? resolvePath2(input) : undefined;
|
|
58909
59138
|
const slug = cloudProjectSlug(pathLike ? cloudProjectPathBasename(input) : input);
|
|
58910
59139
|
const matchGroups = [
|
|
58911
59140
|
uniqueProjectMatches(projects, (project) => project.id.toLowerCase() === normalizedRef),
|
|
58912
|
-
uniqueProjectMatches(projects, (project) => project.path === input || normalizedPath !== undefined &&
|
|
59141
|
+
uniqueProjectMatches(projects, (project) => project.path === input || normalizedPath !== undefined && resolvePath2(project.path) === normalizedPath),
|
|
58913
59142
|
uniqueProjectMatches(projects, (project) => project.name.toLowerCase() === normalizedRef),
|
|
58914
59143
|
uniqueProjectMatches(projects, (project) => project.task_list_id === input || cloudProjectSlug(project.name) === slug || cloudProjectSlug(cloudProjectPathBasename(project.path)) === slug),
|
|
58915
59144
|
uniqueProjectMatches(projects, (project) => project.id.toLowerCase().startsWith(normalizedRef))
|
|
@@ -59493,8 +59722,8 @@ var init_task_crud2 = __esm(() => {
|
|
|
59493
59722
|
});
|
|
59494
59723
|
|
|
59495
59724
|
// src/lib/project-bootstrap.ts
|
|
59496
|
-
import { existsSync as
|
|
59497
|
-
import { basename as basename3, dirname as dirname6, resolve as
|
|
59725
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6, statSync as statSync5 } from "fs";
|
|
59726
|
+
import { basename as basename3, dirname as dirname6, resolve as resolve10 } from "path";
|
|
59498
59727
|
function safeStat(path) {
|
|
59499
59728
|
try {
|
|
59500
59729
|
return statSync5(path);
|
|
@@ -59503,7 +59732,7 @@ function safeStat(path) {
|
|
|
59503
59732
|
}
|
|
59504
59733
|
}
|
|
59505
59734
|
function canonicalPath(input) {
|
|
59506
|
-
const resolved =
|
|
59735
|
+
const resolved = resolve10(input);
|
|
59507
59736
|
const stats = safeStat(resolved);
|
|
59508
59737
|
if (stats?.isFile())
|
|
59509
59738
|
return dirname6(resolved);
|
|
@@ -59512,7 +59741,7 @@ function canonicalPath(input) {
|
|
|
59512
59741
|
function findUp(start, marker) {
|
|
59513
59742
|
let current = canonicalPath(start);
|
|
59514
59743
|
while (true) {
|
|
59515
|
-
if (
|
|
59744
|
+
if (existsSync10(resolve10(current, marker)))
|
|
59516
59745
|
return current;
|
|
59517
59746
|
const parent = dirname6(current);
|
|
59518
59747
|
if (parent === current)
|
|
@@ -59523,8 +59752,8 @@ function findUp(start, marker) {
|
|
|
59523
59752
|
function readPackageJson(path) {
|
|
59524
59753
|
if (!path)
|
|
59525
59754
|
return null;
|
|
59526
|
-
const file =
|
|
59527
|
-
if (!
|
|
59755
|
+
const file = resolve10(path, "package.json");
|
|
59756
|
+
if (!existsSync10(file))
|
|
59528
59757
|
return null;
|
|
59529
59758
|
try {
|
|
59530
59759
|
const parsed = JSON.parse(readFileSync6(file, "utf-8"));
|
|
@@ -59546,7 +59775,7 @@ function workspaceMarker(root, rootPackage) {
|
|
|
59546
59775
|
if (rootPackage?.workspaces)
|
|
59547
59776
|
markers.push("package.json#workspaces");
|
|
59548
59777
|
for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
|
|
59549
|
-
if (
|
|
59778
|
+
if (existsSync10(resolve10(root, marker)))
|
|
59550
59779
|
markers.push(marker);
|
|
59551
59780
|
}
|
|
59552
59781
|
const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
|
|
@@ -59859,7 +60088,7 @@ var init_tags = __esm(() => {
|
|
|
59859
60088
|
});
|
|
59860
60089
|
|
|
59861
60090
|
// src/lib/retention-cleanup.ts
|
|
59862
|
-
import { existsSync as
|
|
60091
|
+
import { existsSync as existsSync11, unlinkSync } from "fs";
|
|
59863
60092
|
function normalizeScopes(scopes) {
|
|
59864
60093
|
if (!scopes || scopes.length === 0)
|
|
59865
60094
|
return [...ALL_SCOPES];
|
|
@@ -60062,7 +60291,7 @@ function applyRetentionCleanup(input, db) {
|
|
|
60062
60291
|
for (const artifact of report.candidates.artifact_files) {
|
|
60063
60292
|
try {
|
|
60064
60293
|
const path = artifactStorePath(artifact.relative_path);
|
|
60065
|
-
if (!
|
|
60294
|
+
if (!existsSync11(path)) {
|
|
60066
60295
|
report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
|
|
60067
60296
|
continue;
|
|
60068
60297
|
}
|
|
@@ -60089,8 +60318,8 @@ var init_retention_cleanup = __esm(() => {
|
|
|
60089
60318
|
});
|
|
60090
60319
|
|
|
60091
60320
|
// src/lib/mention-resolver.ts
|
|
60092
|
-
import { existsSync as
|
|
60093
|
-
import { basename as basename4, isAbsolute, join as
|
|
60321
|
+
import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync6 } from "fs";
|
|
60322
|
+
import { basename as basename4, isAbsolute, join as join12, relative as relative3, resolve as resolve11, sep as sep3 } from "path";
|
|
60094
60323
|
function blankResolution(parsed) {
|
|
60095
60324
|
return {
|
|
60096
60325
|
input: parsed.input,
|
|
@@ -60113,7 +60342,7 @@ function backlink(kind, key2, label, target = key2) {
|
|
|
60113
60342
|
return { kind, key: key2, label, target };
|
|
60114
60343
|
}
|
|
60115
60344
|
function normalizeWorkspace(workspace) {
|
|
60116
|
-
return
|
|
60345
|
+
return resolve11(workspace || process.cwd());
|
|
60117
60346
|
}
|
|
60118
60347
|
function isInside(root, absolutePath) {
|
|
60119
60348
|
const rel = relative3(root, absolutePath);
|
|
@@ -60181,14 +60410,14 @@ function resolveFile(parsed, workspace) {
|
|
|
60181
60410
|
resolution.warnings.push("path is empty or escapes the workspace");
|
|
60182
60411
|
return resolution;
|
|
60183
60412
|
}
|
|
60184
|
-
const absolutePath =
|
|
60413
|
+
const absolutePath = resolve11(workspace, relPath);
|
|
60185
60414
|
if (!isInside(workspace, absolutePath)) {
|
|
60186
60415
|
resolution.path = relPath;
|
|
60187
60416
|
resolution.warnings.push("path escapes the workspace");
|
|
60188
60417
|
return resolution;
|
|
60189
60418
|
}
|
|
60190
60419
|
resolution.path = relPath;
|
|
60191
|
-
if (!
|
|
60420
|
+
if (!existsSync12(absolutePath)) {
|
|
60192
60421
|
resolution.warnings.push("file does not exist in the local workspace");
|
|
60193
60422
|
return resolution;
|
|
60194
60423
|
}
|
|
@@ -60221,7 +60450,7 @@ function walkSourceFiles(root, current = root, files = []) {
|
|
|
60221
60450
|
if (SKIP_DIRS.has(entry2.name))
|
|
60222
60451
|
continue;
|
|
60223
60452
|
}
|
|
60224
|
-
const absolutePath =
|
|
60453
|
+
const absolutePath = join12(current, entry2.name);
|
|
60225
60454
|
if (entry2.isDirectory()) {
|
|
60226
60455
|
if (!SKIP_DIRS.has(entry2.name))
|
|
60227
60456
|
walkSourceFiles(root, absolutePath, files);
|
|
@@ -60526,9 +60755,9 @@ var init_mention_resolver = __esm(() => {
|
|
|
60526
60755
|
});
|
|
60527
60756
|
|
|
60528
60757
|
// src/lib/policy-packs.ts
|
|
60529
|
-
import { relative as relative4, resolve as
|
|
60758
|
+
import { relative as relative4, resolve as resolve12 } from "path";
|
|
60530
60759
|
function normalizePath3(path) {
|
|
60531
|
-
return
|
|
60760
|
+
return resolve12(path);
|
|
60532
60761
|
}
|
|
60533
60762
|
function unique4(values) {
|
|
60534
60763
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -60583,7 +60812,7 @@ function commandMatches(commands, pattern) {
|
|
|
60583
60812
|
}
|
|
60584
60813
|
function pathMatches(paths, pattern, root) {
|
|
60585
60814
|
return paths.filter((path) => {
|
|
60586
|
-
const candidate = path.startsWith("/") ? path :
|
|
60815
|
+
const candidate = path.startsWith("/") ? path : resolve12(root, path);
|
|
60587
60816
|
if (!isPathInside3(root, candidate))
|
|
60588
60817
|
return matchesPattern3(path, pattern);
|
|
60589
60818
|
return matchesPattern3(path, pattern) || matchesPattern3(relative4(root, candidate), pattern);
|
|
@@ -63962,7 +64191,7 @@ var init_audit_ledger = __esm(() => {
|
|
|
63962
64191
|
|
|
63963
64192
|
// src/lib/release-compatibility.ts
|
|
63964
64193
|
import { readFileSync as readFileSync8 } from "fs";
|
|
63965
|
-
import { join as
|
|
64194
|
+
import { join as join13, resolve as resolve13 } from "path";
|
|
63966
64195
|
import { Database as Database2 } from "bun:sqlite";
|
|
63967
64196
|
function pass(id, message, details) {
|
|
63968
64197
|
return { id, status: "passed", message, details };
|
|
@@ -63974,7 +64203,7 @@ function warn(id, message, details) {
|
|
|
63974
64203
|
return { id, status: "warning", message, details };
|
|
63975
64204
|
}
|
|
63976
64205
|
function readPackageJson2(root) {
|
|
63977
|
-
return JSON.parse(readFileSync8(
|
|
64206
|
+
return JSON.parse(readFileSync8(join13(root, "package.json"), "utf8"));
|
|
63978
64207
|
}
|
|
63979
64208
|
function sortedKeys(value) {
|
|
63980
64209
|
return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
|
|
@@ -64070,7 +64299,7 @@ function checkChangelog() {
|
|
|
64070
64299
|
];
|
|
64071
64300
|
}
|
|
64072
64301
|
function createReleaseCompatibilityReport(options = {}) {
|
|
64073
|
-
const root =
|
|
64302
|
+
const root = resolve13(options.root ?? process.cwd());
|
|
64074
64303
|
const packageJson = readPackageJson2(root);
|
|
64075
64304
|
const simulatedLevels = options.simulated_levels ?? defaultSimulationLevels();
|
|
64076
64305
|
const checks4 = [
|
|
@@ -91181,7 +91410,7 @@ var require_protocol = __commonJS((exports) => {
|
|
|
91181
91410
|
return;
|
|
91182
91411
|
}
|
|
91183
91412
|
const pollInterval = task3.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
91184
|
-
await new Promise((
|
|
91413
|
+
await new Promise((resolve14) => setTimeout(resolve14, pollInterval));
|
|
91185
91414
|
options?.signal?.throwIfAborted();
|
|
91186
91415
|
}
|
|
91187
91416
|
} catch (error3) {
|
|
@@ -91193,7 +91422,7 @@ var require_protocol = __commonJS((exports) => {
|
|
|
91193
91422
|
}
|
|
91194
91423
|
request(request, resultSchema, options) {
|
|
91195
91424
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task: task2, relatedTask } = options ?? {};
|
|
91196
|
-
return new Promise((
|
|
91425
|
+
return new Promise((resolve14, reject) => {
|
|
91197
91426
|
const earlyReject = (error3) => {
|
|
91198
91427
|
reject(error3);
|
|
91199
91428
|
};
|
|
@@ -91271,7 +91500,7 @@ var require_protocol = __commonJS((exports) => {
|
|
|
91271
91500
|
if (!parseResult.success) {
|
|
91272
91501
|
reject(parseResult.error);
|
|
91273
91502
|
} else {
|
|
91274
|
-
|
|
91503
|
+
resolve14(parseResult.data);
|
|
91275
91504
|
}
|
|
91276
91505
|
} catch (error3) {
|
|
91277
91506
|
reject(error3);
|
|
@@ -91462,12 +91691,12 @@ var require_protocol = __commonJS((exports) => {
|
|
|
91462
91691
|
interval = task2.pollInterval;
|
|
91463
91692
|
}
|
|
91464
91693
|
} catch {}
|
|
91465
|
-
return new Promise((
|
|
91694
|
+
return new Promise((resolve14, reject) => {
|
|
91466
91695
|
if (signal.aborted) {
|
|
91467
91696
|
reject(new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, "Request cancelled"));
|
|
91468
91697
|
return;
|
|
91469
91698
|
}
|
|
91470
|
-
const timeoutId = setTimeout(
|
|
91699
|
+
const timeoutId = setTimeout(resolve14, interval);
|
|
91471
91700
|
signal.addEventListener("abort", () => {
|
|
91472
91701
|
clearTimeout(timeoutId);
|
|
91473
91702
|
reject(new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -92661,7 +92890,7 @@ var require_mcp = __commonJS((exports) => {
|
|
|
92661
92890
|
let task2 = createTaskResult.task;
|
|
92662
92891
|
const pollInterval = task2.pollInterval ?? 5000;
|
|
92663
92892
|
while (task2.status !== "completed" && task2.status !== "failed" && task2.status !== "cancelled") {
|
|
92664
|
-
await new Promise((
|
|
92893
|
+
await new Promise((resolve14) => setTimeout(resolve14, pollInterval));
|
|
92665
92894
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
92666
92895
|
if (!updatedTask) {
|
|
92667
92896
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -93980,7 +94209,7 @@ var init_agent_run_dispatcher = __esm(() => {
|
|
|
93980
94209
|
});
|
|
93981
94210
|
|
|
93982
94211
|
// src/lib/verification-providers.ts
|
|
93983
|
-
import { existsSync as
|
|
94212
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
93984
94213
|
function normalizeName5(name) {
|
|
93985
94214
|
const normalized = name.trim().toLowerCase();
|
|
93986
94215
|
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
|
|
@@ -94077,7 +94306,7 @@ function classifyLog(text) {
|
|
|
94077
94306
|
async function sleep2(ms) {
|
|
94078
94307
|
if (ms <= 0)
|
|
94079
94308
|
return;
|
|
94080
|
-
await new Promise((
|
|
94309
|
+
await new Promise((resolve14) => setTimeout(resolve14, ms));
|
|
94081
94310
|
}
|
|
94082
94311
|
async function runCommandProvider(provider, input) {
|
|
94083
94312
|
const commandTemplate = input.command || provider.command;
|
|
@@ -94132,7 +94361,7 @@ Timed out after ${provider.timeout_ms}ms`);
|
|
|
94132
94361
|
};
|
|
94133
94362
|
}
|
|
94134
94363
|
function runCiLogProvider(input) {
|
|
94135
|
-
const text = input.log_text ?? (input.log_path &&
|
|
94364
|
+
const text = input.log_text ?? (input.log_path && existsSync13(input.log_path) ? readFileSync9(input.log_path, "utf-8") : "");
|
|
94136
94365
|
return {
|
|
94137
94366
|
status: classifyLog(text),
|
|
94138
94367
|
attempts: 1,
|
|
@@ -94144,7 +94373,7 @@ function runBrowserProvider(input) {
|
|
|
94144
94373
|
if (!input.artifact_path) {
|
|
94145
94374
|
return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
|
|
94146
94375
|
}
|
|
94147
|
-
if (!
|
|
94376
|
+
if (!existsSync13(input.artifact_path)) {
|
|
94148
94377
|
return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
|
|
94149
94378
|
}
|
|
94150
94379
|
return {
|
|
@@ -96874,7 +97103,7 @@ var init_local_bridge = __esm(() => {
|
|
|
96874
97103
|
// src/lib/local-backups.ts
|
|
96875
97104
|
import { createHash as createHash12 } from "crypto";
|
|
96876
97105
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
|
|
96877
|
-
import { dirname as dirname7, resolve as
|
|
97106
|
+
import { dirname as dirname7, resolve as resolve14 } from "path";
|
|
96878
97107
|
import { mkdirSync as mkdirSync6 } from "fs";
|
|
96879
97108
|
function stableJson2(value) {
|
|
96880
97109
|
if (value === null || typeof value !== "object")
|
|
@@ -96976,14 +97205,14 @@ function createLocalBackup(options = {}, db) {
|
|
|
96976
97205
|
return backup;
|
|
96977
97206
|
}
|
|
96978
97207
|
function writeLocalBackupFile(backup, outputPath) {
|
|
96979
|
-
const path =
|
|
97208
|
+
const path = resolve14(outputPath);
|
|
96980
97209
|
mkdirSync6(dirname7(path), { recursive: true });
|
|
96981
97210
|
writeFileSync3(path, `${JSON.stringify(backup, null, 2)}
|
|
96982
97211
|
`);
|
|
96983
97212
|
return path;
|
|
96984
97213
|
}
|
|
96985
97214
|
function readLocalBackupFile(path) {
|
|
96986
|
-
return JSON.parse(readFileSync10(
|
|
97215
|
+
return JSON.parse(readFileSync10(resolve14(path), "utf-8"));
|
|
96987
97216
|
}
|
|
96988
97217
|
function verifyLocalBackup(value, options = {}, db) {
|
|
96989
97218
|
const verifiedAt = options.verified_at ?? now();
|
|
@@ -98435,8 +98664,8 @@ __export(exports_local_extensions, {
|
|
|
98435
98664
|
discoverLocalExtensions: () => discoverLocalExtensions
|
|
98436
98665
|
});
|
|
98437
98666
|
import { createHash as createHash15, createVerify } from "crypto";
|
|
98438
|
-
import { existsSync as
|
|
98439
|
-
import { basename as basename5, join as
|
|
98667
|
+
import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync11, statSync as statSync7 } from "fs";
|
|
98668
|
+
import { basename as basename5, join as join14, resolve as resolve15 } from "path";
|
|
98440
98669
|
function isObject3(value) {
|
|
98441
98670
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
98442
98671
|
}
|
|
@@ -98694,11 +98923,11 @@ function verifyExtensionSignature(input) {
|
|
|
98694
98923
|
return verifier.verify(input.public_key, decodeSignature(input.signature));
|
|
98695
98924
|
}
|
|
98696
98925
|
function inspectExtensionSource(source3) {
|
|
98697
|
-
const resolved =
|
|
98698
|
-
if (!
|
|
98926
|
+
const resolved = resolve15(source3);
|
|
98927
|
+
if (!existsSync14(resolved))
|
|
98699
98928
|
throw new Error(`extension source not found: ${source3}`);
|
|
98700
98929
|
const stat = statSync7(resolved);
|
|
98701
|
-
const manifestPath = stat.isDirectory() ? [
|
|
98930
|
+
const manifestPath = stat.isDirectory() ? [join14(resolved, "todos.extension.json"), join14(resolved, "extension.json")].find(existsSync14) : resolved;
|
|
98702
98931
|
if (!manifestPath)
|
|
98703
98932
|
throw new Error(`extension directory ${source3} is missing todos.extension.json`);
|
|
98704
98933
|
const raw = readFileSync11(manifestPath);
|
|
@@ -98792,26 +99021,26 @@ function testExtensionCompatibility(sourceOrManifest) {
|
|
|
98792
99021
|
function projectExtensionSources(projectPath) {
|
|
98793
99022
|
if (!projectPath)
|
|
98794
99023
|
return [];
|
|
98795
|
-
const root =
|
|
99024
|
+
const root = resolve15(projectPath);
|
|
98796
99025
|
const candidates = [
|
|
98797
|
-
|
|
98798
|
-
|
|
99026
|
+
join14(root, "todos.extension.json"),
|
|
99027
|
+
join14(root, ".todos", "todos.extension.json")
|
|
98799
99028
|
];
|
|
98800
|
-
const extensionDir =
|
|
98801
|
-
if (
|
|
99029
|
+
const extensionDir = join14(root, ".todos", "extensions");
|
|
99030
|
+
if (existsSync14(extensionDir)) {
|
|
98802
99031
|
for (const entry2 of readdirSync3(extensionDir)) {
|
|
98803
99032
|
if (entry2.startsWith("."))
|
|
98804
99033
|
continue;
|
|
98805
|
-
const full =
|
|
99034
|
+
const full = join14(extensionDir, entry2);
|
|
98806
99035
|
if (statSync7(full).isDirectory() || entry2.endsWith(".json"))
|
|
98807
99036
|
candidates.push(full);
|
|
98808
99037
|
}
|
|
98809
99038
|
}
|
|
98810
|
-
return candidates.filter(
|
|
99039
|
+
return candidates.filter(existsSync14);
|
|
98811
99040
|
}
|
|
98812
99041
|
function discoverLocalExtensions(options = {}) {
|
|
98813
99042
|
const config2 = loadConfig();
|
|
98814
|
-
const projectPath = options.project_path ?
|
|
99043
|
+
const projectPath = options.project_path ? resolve15(options.project_path) : null;
|
|
98815
99044
|
const configuredSources = [
|
|
98816
99045
|
...config2.extension_sources || [],
|
|
98817
99046
|
...projectPath ? config2.project_overrides?.[projectPath]?.extension_sources || [] : []
|
|
@@ -98819,7 +99048,7 @@ function discoverLocalExtensions(options = {}) {
|
|
|
98819
99048
|
const sources = Array.from(new Set([
|
|
98820
99049
|
...configuredSources,
|
|
98821
99050
|
...projectExtensionSources(projectPath || undefined)
|
|
98822
|
-
])).map((source3) => projectPath && !source3.startsWith("/") ?
|
|
99051
|
+
])).map((source3) => projectPath && !source3.startsWith("/") ? resolve15(projectPath, source3) : resolve15(source3));
|
|
98823
99052
|
const warnings = [];
|
|
98824
99053
|
const discovered = [];
|
|
98825
99054
|
for (const source3 of sources) {
|
|
@@ -103214,9 +103443,9 @@ __export(exports_extract, {
|
|
|
103214
103443
|
buildCodebaseIndex: () => buildCodebaseIndex,
|
|
103215
103444
|
EXTRACT_TAGS: () => EXTRACT_TAGS
|
|
103216
103445
|
});
|
|
103217
|
-
import { existsSync as
|
|
103446
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12, statSync as statSync8 } from "fs";
|
|
103218
103447
|
import { createHash as createHash17 } from "crypto";
|
|
103219
|
-
import { relative as relative5, resolve as
|
|
103448
|
+
import { relative as relative5, resolve as resolve16, join as join15 } from "path";
|
|
103220
103449
|
function stableHash(value) {
|
|
103221
103450
|
return createHash17("sha256").update(value).digest("hex");
|
|
103222
103451
|
}
|
|
@@ -103224,9 +103453,9 @@ function normalizePathForMatch(value) {
|
|
|
103224
103453
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
103225
103454
|
}
|
|
103226
103455
|
function readGitignorePatterns(basePath) {
|
|
103227
|
-
const root = statSync8(basePath).isFile() ?
|
|
103228
|
-
const gitignorePath =
|
|
103229
|
-
if (!
|
|
103456
|
+
const root = statSync8(basePath).isFile() ? resolve16(basePath, "..") : basePath;
|
|
103457
|
+
const gitignorePath = join15(root, ".gitignore");
|
|
103458
|
+
if (!existsSync15(gitignorePath))
|
|
103230
103459
|
return [];
|
|
103231
103460
|
try {
|
|
103232
103461
|
return readFileSync12(gitignorePath, "utf-8").split(`
|
|
@@ -103360,7 +103589,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
|
|
|
103360
103589
|
return files.sort();
|
|
103361
103590
|
}
|
|
103362
103591
|
function buildCodebaseIndex(options) {
|
|
103363
|
-
const basePath =
|
|
103592
|
+
const basePath = resolve16(options.path);
|
|
103364
103593
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
103365
103594
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
103366
103595
|
const excludes = options.exclude || [];
|
|
@@ -103368,10 +103597,10 @@ function buildCodebaseIndex(options) {
|
|
|
103368
103597
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
103369
103598
|
const indexed = [];
|
|
103370
103599
|
for (const file of files) {
|
|
103371
|
-
const fullPath = statSync8(basePath).isFile() ? basePath :
|
|
103600
|
+
const fullPath = statSync8(basePath).isFile() ? basePath : join15(basePath, file);
|
|
103372
103601
|
try {
|
|
103373
103602
|
const source3 = readFileSync12(fullPath, "utf-8");
|
|
103374
|
-
const relPath = statSync8(basePath).isFile() ? relative5(
|
|
103603
|
+
const relPath = statSync8(basePath).isFile() ? relative5(resolve16(basePath, ".."), fullPath) : file;
|
|
103375
103604
|
indexed.push({
|
|
103376
103605
|
file: relPath,
|
|
103377
103606
|
checksum: stableHash(source3).slice(0, 24),
|
|
@@ -103391,7 +103620,7 @@ function buildCodebaseIndex(options) {
|
|
|
103391
103620
|
};
|
|
103392
103621
|
}
|
|
103393
103622
|
function extractTodos(options, db) {
|
|
103394
|
-
const basePath =
|
|
103623
|
+
const basePath = resolve16(options.path);
|
|
103395
103624
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
103396
103625
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
103397
103626
|
const excludes = options.exclude || [];
|
|
@@ -103399,10 +103628,10 @@ function extractTodos(options, db) {
|
|
|
103399
103628
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
103400
103629
|
const allComments = [];
|
|
103401
103630
|
for (const file of files) {
|
|
103402
|
-
const fullPath = statSync8(basePath).isFile() ? basePath :
|
|
103631
|
+
const fullPath = statSync8(basePath).isFile() ? basePath : join15(basePath, file);
|
|
103403
103632
|
try {
|
|
103404
103633
|
const source3 = readFileSync12(fullPath, "utf-8");
|
|
103405
|
-
const relPath = statSync8(basePath).isFile() ? relative5(
|
|
103634
|
+
const relPath = statSync8(basePath).isFile() ? relative5(resolve16(basePath, ".."), fullPath) : file;
|
|
103406
103635
|
const comments = extractFromSource(source3, relPath, tags);
|
|
103407
103636
|
allComments.push(...comments);
|
|
103408
103637
|
} catch {}
|
|
@@ -103496,7 +103725,7 @@ async function watchSourceTodos(options, onRun) {
|
|
|
103496
103725
|
const interval = Math.max(100, options.interval_ms || 2000);
|
|
103497
103726
|
const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
|
|
103498
103727
|
const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
|
|
103499
|
-
const root =
|
|
103728
|
+
const root = resolve16(options.path);
|
|
103500
103729
|
const runs = [];
|
|
103501
103730
|
let previous = new Map;
|
|
103502
103731
|
for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
|
|
@@ -104055,7 +104284,7 @@ Last seen: ${agent.last_seen_at}`
|
|
|
104055
104284
|
`Suggested names: ${suggestions.slice(0, 8).join(", ")}`,
|
|
104056
104285
|
allActive.length > 0 ? `Active agents (avoid these names): ${allActive.map((a) => `${a.name} (seen ${Math.round((Date.now() - new Date(a.last_seen_at).getTime()) / 60000)}m ago)`).join(", ")}` : "No active agents.",
|
|
104057
104286
|
`
|
|
104058
|
-
To restrict names, configure agent_pool or project_pools in
|
|
104287
|
+
To restrict names, configure agent_pool or project_pools in the todos data home config file (default <data home>/config.json)`
|
|
104059
104288
|
];
|
|
104060
104289
|
return { content: [{ type: "text", text: lines2.join(`
|
|
104061
104290
|
`) }] };
|
|
@@ -104358,7 +104587,7 @@ __export(exports_builtin_templates, {
|
|
|
104358
104587
|
BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
|
|
104359
104588
|
});
|
|
104360
104589
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
104361
|
-
import { join as
|
|
104590
|
+
import { join as join16 } from "path";
|
|
104362
104591
|
function templateMetadata(template) {
|
|
104363
104592
|
return {
|
|
104364
104593
|
source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
|
|
@@ -104417,7 +104646,7 @@ function writeBuiltinTemplateFiles(directory) {
|
|
|
104417
104646
|
mkdirSync7(directory, { recursive: true });
|
|
104418
104647
|
const files = [];
|
|
104419
104648
|
for (const entry2 of exportBuiltinTemplateFiles()) {
|
|
104420
|
-
const path =
|
|
104649
|
+
const path = join16(directory, entry2.filename);
|
|
104421
104650
|
writeFileSync4(path, `${JSON.stringify(entry2.template, null, 2)}
|
|
104422
104651
|
`, "utf-8");
|
|
104423
104652
|
files.push(path);
|
|
@@ -104943,16 +105172,16 @@ __export(exports_environment_snapshots, {
|
|
|
104943
105172
|
captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
|
|
104944
105173
|
});
|
|
104945
105174
|
import { createHash as createHash18 } from "crypto";
|
|
104946
|
-
import { existsSync as
|
|
105175
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13, statSync as statSync9 } from "fs";
|
|
104947
105176
|
import { hostname as hostname3, platform, arch } from "os";
|
|
104948
|
-
import { dirname as dirname8, join as
|
|
105177
|
+
import { dirname as dirname8, join as join17, resolve as resolve17 } from "path";
|
|
104949
105178
|
import { tmpdir as tmpdir3 } from "os";
|
|
104950
105179
|
function sha2567(value) {
|
|
104951
105180
|
return createHash18("sha256").update(value).digest("hex");
|
|
104952
105181
|
}
|
|
104953
105182
|
function fileRecord(root, relativePath) {
|
|
104954
|
-
const path =
|
|
104955
|
-
if (!
|
|
105183
|
+
const path = join17(root, relativePath);
|
|
105184
|
+
if (!existsSync16(path))
|
|
104956
105185
|
return null;
|
|
104957
105186
|
const stat = statSync9(path);
|
|
104958
105187
|
if (!stat.isFile())
|
|
@@ -104964,7 +105193,7 @@ function manifestRecord(root, relativePath) {
|
|
|
104964
105193
|
const base = fileRecord(root, relativePath);
|
|
104965
105194
|
if (!base)
|
|
104966
105195
|
return null;
|
|
104967
|
-
const parsed = readJsonFile(
|
|
105196
|
+
const parsed = readJsonFile(join17(root, relativePath));
|
|
104968
105197
|
if (!parsed)
|
|
104969
105198
|
return { ...base, redacted: {} };
|
|
104970
105199
|
const redacted = redactValue({
|
|
@@ -105059,15 +105288,15 @@ function commandEnv(env, includeValues) {
|
|
|
105059
105288
|
function defaultSnapshotDir() {
|
|
105060
105289
|
const dbPath = getDatabasePath();
|
|
105061
105290
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
105062
|
-
return
|
|
105063
|
-
return
|
|
105291
|
+
return join17(tmpdir3(), "hasna-todos", "environment-snapshots");
|
|
105292
|
+
return join17(dirname8(resolve17(dbPath)), "environment-snapshots");
|
|
105064
105293
|
}
|
|
105065
105294
|
function snapshotWithId(snapshot) {
|
|
105066
105295
|
const digest4 = sha2567(JSON.stringify(snapshot)).slice(0, 24);
|
|
105067
105296
|
return { id: `env_${digest4}`, ...snapshot };
|
|
105068
105297
|
}
|
|
105069
105298
|
function captureEnvironmentSnapshot(input = {}) {
|
|
105070
|
-
const root =
|
|
105299
|
+
const root = resolve17(input.root || process.cwd());
|
|
105071
105300
|
const env = input.env || process.env;
|
|
105072
105301
|
const warnings = [];
|
|
105073
105302
|
const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
|
|
@@ -105107,13 +105336,13 @@ function captureEnvironmentSnapshot(input = {}) {
|
|
|
105107
105336
|
});
|
|
105108
105337
|
}
|
|
105109
105338
|
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
105110
|
-
const path = outputPath ?
|
|
105339
|
+
const path = outputPath ? resolve17(outputPath) : join17(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
105111
105340
|
ensureDir(dirname8(path));
|
|
105112
105341
|
writeJsonFile(path, snapshot);
|
|
105113
105342
|
return path;
|
|
105114
105343
|
}
|
|
105115
105344
|
function readEnvironmentSnapshot(path) {
|
|
105116
|
-
const snapshot = readJsonFile(
|
|
105345
|
+
const snapshot = readJsonFile(resolve17(path));
|
|
105117
105346
|
if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
|
|
105118
105347
|
throw new Error(`Invalid environment snapshot: ${path}`);
|
|
105119
105348
|
}
|
|
@@ -105939,8 +106168,8 @@ class SqlitePrGroupLedgerPersistence {
|
|
|
105939
106168
|
async transaction(fn) {
|
|
105940
106169
|
const previous = sqliteTransactionTails2.get(this.db) ?? Promise.resolve();
|
|
105941
106170
|
let release;
|
|
105942
|
-
const current = new Promise((
|
|
105943
|
-
release =
|
|
106171
|
+
const current = new Promise((resolve18) => {
|
|
106172
|
+
release = resolve18;
|
|
105944
106173
|
});
|
|
105945
106174
|
sqliteTransactionTails2.set(this.db, current);
|
|
105946
106175
|
await previous;
|
|
@@ -106038,27 +106267,27 @@ __export(exports_serve, {
|
|
|
106038
106267
|
SECURITY_HEADERS: () => SECURITY_HEADERS,
|
|
106039
106268
|
MIME_TYPES: () => MIME_TYPES
|
|
106040
106269
|
});
|
|
106041
|
-
import { existsSync as
|
|
106042
|
-
import { join as
|
|
106270
|
+
import { existsSync as existsSync17 } from "fs";
|
|
106271
|
+
import { join as join18, dirname as dirname9, extname } from "path";
|
|
106043
106272
|
import { fileURLToPath } from "url";
|
|
106044
106273
|
function resolveDashboardDir() {
|
|
106045
106274
|
const candidates = [];
|
|
106046
106275
|
try {
|
|
106047
106276
|
const scriptDir = dirname9(fileURLToPath(import.meta.url));
|
|
106048
|
-
candidates.push(
|
|
106049
|
-
candidates.push(
|
|
106277
|
+
candidates.push(join18(scriptDir, "..", "dashboard", "dist"));
|
|
106278
|
+
candidates.push(join18(scriptDir, "..", "..", "dashboard", "dist"));
|
|
106050
106279
|
} catch {}
|
|
106051
106280
|
if (process.argv[1]) {
|
|
106052
106281
|
const mainDir = dirname9(process.argv[1]);
|
|
106053
|
-
candidates.push(
|
|
106054
|
-
candidates.push(
|
|
106282
|
+
candidates.push(join18(mainDir, "..", "dashboard", "dist"));
|
|
106283
|
+
candidates.push(join18(mainDir, "..", "..", "dashboard", "dist"));
|
|
106055
106284
|
}
|
|
106056
|
-
candidates.push(
|
|
106285
|
+
candidates.push(join18(process.cwd(), "dashboard", "dist"));
|
|
106057
106286
|
for (const candidate of candidates) {
|
|
106058
|
-
if (
|
|
106287
|
+
if (existsSync17(candidate))
|
|
106059
106288
|
return candidate;
|
|
106060
106289
|
}
|
|
106061
|
-
return
|
|
106290
|
+
return join18(process.cwd(), "dashboard", "dist");
|
|
106062
106291
|
}
|
|
106063
106292
|
function getProvidedApiKey(req) {
|
|
106064
106293
|
const headerKey = req.headers.get("x-api-key");
|
|
@@ -106130,7 +106359,7 @@ function json4(data, status3 = 200, headers) {
|
|
|
106130
106359
|
});
|
|
106131
106360
|
}
|
|
106132
106361
|
function serveStaticFile(filePath) {
|
|
106133
|
-
if (!
|
|
106362
|
+
if (!existsSync17(filePath))
|
|
106134
106363
|
return null;
|
|
106135
106364
|
const ext = extname(filePath);
|
|
106136
106365
|
const contentType2 = MIME_TYPES[ext] || "application/octet-stream";
|
|
@@ -106231,7 +106460,7 @@ data: ${data}
|
|
|
106231
106460
|
filteredSseClients.delete(client);
|
|
106232
106461
|
}
|
|
106233
106462
|
const dashboardDir = resolveDashboardDir();
|
|
106234
|
-
const dashboardExists =
|
|
106463
|
+
const dashboardExists = existsSync17(dashboardDir);
|
|
106235
106464
|
if (!dashboardExists) {
|
|
106236
106465
|
console.error(`
|
|
106237
106466
|
Dashboard not found at: ${dashboardDir}`);
|