@hasna/todos 0.15.49 → 0.15.51
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/commands/query-commands.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +482 -336
- package/dist/contracts.js +193 -94
- package/dist/db/task-crud.d.ts.map +1 -1
- package/dist/index.js +394 -295
- 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 +268 -170
- package/dist/mcp.js +6 -3
- package/dist/project-registration.js +191 -92
- package/dist/registry.js +193 -94
- package/dist/release-provenance.json +5 -5
- package/dist/sdk/index.js +100 -8
- package/dist/server/index.js +437 -216
- 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 +6 -3
- package/postinstall.js +34 -0
|
@@ -323,23 +323,118 @@ var init_types = __esm(() => {
|
|
|
323
323
|
};
|
|
324
324
|
});
|
|
325
325
|
|
|
326
|
-
//
|
|
327
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
328
|
-
import { createHash } from "crypto";
|
|
326
|
+
// node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
|
|
329
327
|
import { homedir } from "os";
|
|
330
328
|
import { join } from "path";
|
|
329
|
+
function assertApp(app) {
|
|
330
|
+
if (typeof app !== "string" || app.length === 0) {
|
|
331
|
+
throw new TypeError("paths: app must be a non-empty string");
|
|
332
|
+
}
|
|
333
|
+
if (!APP_SLUG_RE.test(app)) {
|
|
334
|
+
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function envOf(options) {
|
|
338
|
+
return options.env ?? process.env;
|
|
339
|
+
}
|
|
340
|
+
function envValue(options, kind) {
|
|
341
|
+
const value = envOf(options)[KIND_ENV[kind]];
|
|
342
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
343
|
+
}
|
|
344
|
+
function isMacOS(platform) {
|
|
345
|
+
return platform === "darwin";
|
|
346
|
+
}
|
|
347
|
+
function baseDir(kind, options) {
|
|
348
|
+
const override = envValue(options, kind);
|
|
349
|
+
if (override)
|
|
350
|
+
return override;
|
|
351
|
+
const home = options.home ?? homedir();
|
|
352
|
+
const platform = options.platform ?? process.platform;
|
|
353
|
+
if (isMacOS(platform)) {
|
|
354
|
+
switch (kind) {
|
|
355
|
+
case "config":
|
|
356
|
+
case "data":
|
|
357
|
+
return join(home, "Library", "Application Support", "Hasna");
|
|
358
|
+
case "cache":
|
|
359
|
+
return join(home, "Library", "Caches", "Hasna");
|
|
360
|
+
case "state":
|
|
361
|
+
return join(home, "Library", "Logs", "Hasna");
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
switch (kind) {
|
|
365
|
+
case "config":
|
|
366
|
+
return join(home, ".config", "hasna");
|
|
367
|
+
case "data":
|
|
368
|
+
return join(home, ".local", "share", "hasna");
|
|
369
|
+
case "state":
|
|
370
|
+
return join(home, ".local", "state", "hasna");
|
|
371
|
+
case "cache":
|
|
372
|
+
return join(home, ".cache", "hasna");
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function resolvePath(kind, options) {
|
|
376
|
+
assertApp(options.app);
|
|
377
|
+
const appSegment = options.internal === true ? join("internal", options.app) : options.app;
|
|
378
|
+
return join(baseDir(kind, options), appSegment);
|
|
379
|
+
}
|
|
380
|
+
function dataDir(options) {
|
|
381
|
+
return resolvePath("data", options);
|
|
382
|
+
}
|
|
383
|
+
var KIND_ENV, APP_SLUG_RE;
|
|
384
|
+
var init_dist = __esm(() => {
|
|
385
|
+
KIND_ENV = {
|
|
386
|
+
config: "HASNA_CONFIG_HOME",
|
|
387
|
+
data: "HASNA_DATA_HOME",
|
|
388
|
+
state: "HASNA_STATE_HOME",
|
|
389
|
+
cache: "HASNA_CACHE_HOME"
|
|
390
|
+
};
|
|
391
|
+
APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// src/lib/paths.ts
|
|
395
|
+
import { existsSync } from "fs";
|
|
396
|
+
import { homedir as homedir2 } from "os";
|
|
397
|
+
import { join as join2, resolve } from "path";
|
|
398
|
+
function effectiveHome(env = process.env) {
|
|
399
|
+
return env.HOME || env.USERPROFILE || homedir2();
|
|
400
|
+
}
|
|
401
|
+
function legacyHomeDir(env = process.env) {
|
|
402
|
+
return join2(effectiveHome(env), ".hasna", "todos");
|
|
403
|
+
}
|
|
404
|
+
function resolverHome(env = process.env) {
|
|
405
|
+
return dataDir({ app: "todos", home: effectiveHome(env), env });
|
|
406
|
+
}
|
|
407
|
+
function adoptResolverHome(resolved, env = process.env) {
|
|
408
|
+
const dataOverride = env.HASNA_DATA_HOME;
|
|
409
|
+
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
410
|
+
return true;
|
|
411
|
+
return existsSync(join2(resolved, "todos.db")) || existsSync(join2(resolved, "config.json"));
|
|
412
|
+
}
|
|
413
|
+
function getTodosDir(env = process.env) {
|
|
414
|
+
const resolved = resolverHome(env);
|
|
415
|
+
return resolve(adoptResolverHome(resolved, env) ? resolved : legacyHomeDir(env));
|
|
416
|
+
}
|
|
417
|
+
var init_paths = __esm(() => {
|
|
418
|
+
init_dist();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// src/lib/sync-utils.ts
|
|
422
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
423
|
+
import { createHash } from "crypto";
|
|
424
|
+
import { homedir as homedir3 } from "os";
|
|
425
|
+
import { join as join3 } from "path";
|
|
331
426
|
function getHomeDir() {
|
|
332
|
-
return process.env["HOME"] || process.env["USERPROFILE"] ||
|
|
427
|
+
return process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
|
|
333
428
|
}
|
|
334
429
|
function getTodosGlobalDir() {
|
|
335
|
-
return
|
|
430
|
+
return getTodosDir();
|
|
336
431
|
}
|
|
337
432
|
function ensureDir(dir) {
|
|
338
|
-
if (!
|
|
433
|
+
if (!existsSync2(dir))
|
|
339
434
|
mkdirSync(dir, { recursive: true });
|
|
340
435
|
}
|
|
341
436
|
function listJsonFiles(dir) {
|
|
342
|
-
if (!
|
|
437
|
+
if (!existsSync2(dir))
|
|
343
438
|
return [];
|
|
344
439
|
return readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
345
440
|
}
|
|
@@ -355,14 +450,14 @@ function writeJsonFile(path, data) {
|
|
|
355
450
|
`);
|
|
356
451
|
}
|
|
357
452
|
function readHighWaterMark(dir) {
|
|
358
|
-
const path =
|
|
359
|
-
if (!
|
|
453
|
+
const path = join3(dir, ".highwatermark");
|
|
454
|
+
if (!existsSync2(path))
|
|
360
455
|
return 1;
|
|
361
456
|
const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
|
|
362
457
|
return isNaN(val) ? 1 : val;
|
|
363
458
|
}
|
|
364
459
|
function writeHighWaterMark(dir, value) {
|
|
365
|
-
writeFileSync(
|
|
460
|
+
writeFileSync(join3(dir, ".highwatermark"), String(value));
|
|
366
461
|
}
|
|
367
462
|
function getFileMtimeMs(path) {
|
|
368
463
|
try {
|
|
@@ -417,6 +512,7 @@ function hasSyncFingerprintChanged(record) {
|
|
|
417
512
|
}
|
|
418
513
|
var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
|
|
419
514
|
var init_sync_utils = __esm(() => {
|
|
515
|
+
init_paths();
|
|
420
516
|
HOME = getHomeDir();
|
|
421
517
|
});
|
|
422
518
|
|
|
@@ -429,10 +525,10 @@ var init_creator_identity = __esm(() => {
|
|
|
429
525
|
});
|
|
430
526
|
|
|
431
527
|
// src/lib/config.ts
|
|
432
|
-
import { existsSync as
|
|
433
|
-
import { dirname, join as
|
|
528
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
529
|
+
import { dirname, join as join4 } from "path";
|
|
434
530
|
function getConfigPath() {
|
|
435
|
-
return
|
|
531
|
+
return join4(getTodosGlobalDir(), "config.json");
|
|
436
532
|
}
|
|
437
533
|
function normalizeAgent(agent) {
|
|
438
534
|
return agent.trim().toLowerCase();
|
|
@@ -440,7 +536,7 @@ function normalizeAgent(agent) {
|
|
|
440
536
|
function loadConfig() {
|
|
441
537
|
if (cached)
|
|
442
538
|
return cached;
|
|
443
|
-
if (!
|
|
539
|
+
if (!existsSync3(getConfigPath())) {
|
|
444
540
|
cached = {};
|
|
445
541
|
return cached;
|
|
446
542
|
}
|
|
@@ -463,7 +559,7 @@ function updateConfig(patch) {
|
|
|
463
559
|
}
|
|
464
560
|
function getTodosAiConfig() {
|
|
465
561
|
const configPath = getConfigPath();
|
|
466
|
-
if (!
|
|
562
|
+
if (!existsSync3(configPath))
|
|
467
563
|
return {};
|
|
468
564
|
const parsed = JSON.parse(readFileSync2(configPath, "utf8"));
|
|
469
565
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -653,7 +749,7 @@ var init_redaction = __esm(() => {
|
|
|
653
749
|
});
|
|
654
750
|
|
|
655
751
|
// src/lib/secret-redaction.ts
|
|
656
|
-
import { readFileSync as readFileSync3, existsSync as
|
|
752
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
657
753
|
function registerCustomRedactor(fn) {
|
|
658
754
|
customRedactors.push(fn);
|
|
659
755
|
}
|
|
@@ -718,7 +814,7 @@ function scanAndRedactText(text, options = {}) {
|
|
|
718
814
|
};
|
|
719
815
|
}
|
|
720
816
|
function scanFileForSecrets(path, options = {}) {
|
|
721
|
-
if (!
|
|
817
|
+
if (!existsSync4(path))
|
|
722
818
|
throw new Error(`File not found: ${path}`);
|
|
723
819
|
const content = readFileSync3(path, "utf8");
|
|
724
820
|
return scanAndRedactText(content, options);
|
|
@@ -4400,9 +4496,9 @@ var init_schema = __esm(() => {
|
|
|
4400
4496
|
});
|
|
4401
4497
|
|
|
4402
4498
|
// src/db/machines.ts
|
|
4403
|
-
import { existsSync as
|
|
4499
|
+
import { existsSync as existsSync5 } from "fs";
|
|
4404
4500
|
import { hostname as osHostname, platform as osPlatform, arch as osArch } from "os";
|
|
4405
|
-
import { resolve } from "path";
|
|
4501
|
+
import { resolve as resolve2 } from "path";
|
|
4406
4502
|
import { spawnSync } from "child_process";
|
|
4407
4503
|
function parseMetadata(value) {
|
|
4408
4504
|
if (!value)
|
|
@@ -4433,7 +4529,7 @@ function discoverGitRoot(workspacePath) {
|
|
|
4433
4529
|
}
|
|
4434
4530
|
function topologyMetadata(input, existing = {}) {
|
|
4435
4531
|
const next = { ...existing };
|
|
4436
|
-
const workspacePath = input.workspace_path ?
|
|
4532
|
+
const workspacePath = input.workspace_path ? resolve2(input.workspace_path) : undefined;
|
|
4437
4533
|
const entries = {
|
|
4438
4534
|
tailscale_name: input.tailscale_name,
|
|
4439
4535
|
tailscale_ip: input.tailscale_ip,
|
|
@@ -4610,7 +4706,7 @@ function getMachineTopologyDiagnostics(opts = {}, db, at = new Date) {
|
|
|
4610
4706
|
message: `${project.name} has ${distinctPaths.length} different machine-local paths`
|
|
4611
4707
|
});
|
|
4612
4708
|
}
|
|
4613
|
-
if (localRow && !
|
|
4709
|
+
if (localRow && !existsSync5(localRow.path)) {
|
|
4614
4710
|
pathIssues.push({
|
|
4615
4711
|
type: "path_missing",
|
|
4616
4712
|
project_id: project.id,
|
|
@@ -4621,7 +4717,7 @@ function getMachineTopologyDiagnostics(opts = {}, db, at = new Date) {
|
|
|
4621
4717
|
message: `Local path does not exist on this machine: ${localRow.path}`
|
|
4622
4718
|
});
|
|
4623
4719
|
}
|
|
4624
|
-
if (!localRow && project.path && machineById.has(localMachine.id) && !
|
|
4720
|
+
if (!localRow && project.path && machineById.has(localMachine.id) && !existsSync5(project.path)) {
|
|
4625
4721
|
pathIssues.push({
|
|
4626
4722
|
type: "path_missing",
|
|
4627
4723
|
project_id: project.id,
|
|
@@ -5187,18 +5283,18 @@ __export(exports_database, {
|
|
|
5187
5283
|
LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
|
|
5188
5284
|
});
|
|
5189
5285
|
import { Database } from "bun:sqlite";
|
|
5190
|
-
import { existsSync as
|
|
5191
|
-
import { dirname as dirname2, join as
|
|
5286
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
5287
|
+
import { dirname as dirname2, join as join5, resolve as resolve3 } from "path";
|
|
5192
5288
|
function isInMemoryDb(path) {
|
|
5193
5289
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
5194
5290
|
}
|
|
5195
5291
|
function findNearestProjectDb(startDir) {
|
|
5196
5292
|
const gitRoot = findGitRoot(startDir);
|
|
5197
|
-
const stopAt = gitRoot ?
|
|
5198
|
-
let dir =
|
|
5293
|
+
const stopAt = gitRoot ? resolve3(gitRoot) : resolve3(startDir);
|
|
5294
|
+
let dir = resolve3(startDir);
|
|
5199
5295
|
while (true) {
|
|
5200
|
-
const candidate =
|
|
5201
|
-
if (
|
|
5296
|
+
const candidate = join5(dir, ".hasna", "todos", "todos.db");
|
|
5297
|
+
if (existsSync6(candidate))
|
|
5202
5298
|
return candidate;
|
|
5203
5299
|
if (dir === stopAt)
|
|
5204
5300
|
break;
|
|
@@ -5210,9 +5306,9 @@ function findNearestProjectDb(startDir) {
|
|
|
5210
5306
|
return null;
|
|
5211
5307
|
}
|
|
5212
5308
|
function findGitRoot(startDir) {
|
|
5213
|
-
let dir =
|
|
5309
|
+
let dir = resolve3(startDir);
|
|
5214
5310
|
while (true) {
|
|
5215
|
-
if (
|
|
5311
|
+
if (existsSync6(join5(dir, ".git")))
|
|
5216
5312
|
return dir;
|
|
5217
5313
|
const parent = dirname2(dir);
|
|
5218
5314
|
if (parent === dir)
|
|
@@ -5222,7 +5318,7 @@ function findGitRoot(startDir) {
|
|
|
5222
5318
|
return null;
|
|
5223
5319
|
}
|
|
5224
5320
|
function getGlobalDbPath() {
|
|
5225
|
-
return
|
|
5321
|
+
return join5(getTodosGlobalDir(), "todos.db");
|
|
5226
5322
|
}
|
|
5227
5323
|
function hasExplicitProjectArg(args = process.argv.slice(2)) {
|
|
5228
5324
|
return args.some((arg) => arg === "--project" || arg.startsWith("--project="));
|
|
@@ -5260,7 +5356,7 @@ function getDbPath() {
|
|
|
5260
5356
|
if (process.env["TODOS_DB_SCOPE"] === "project") {
|
|
5261
5357
|
const gitRoot = findGitRoot(cwd);
|
|
5262
5358
|
if (gitRoot && canCreateScopedProjectDb()) {
|
|
5263
|
-
return
|
|
5359
|
+
return join5(gitRoot, ".hasna", "todos", "todos.db");
|
|
5264
5360
|
}
|
|
5265
5361
|
}
|
|
5266
5362
|
return getGlobalDbPath();
|
|
@@ -5271,8 +5367,8 @@ function getDatabasePath() {
|
|
|
5271
5367
|
function ensureDir2(filePath) {
|
|
5272
5368
|
if (isInMemoryDb(filePath))
|
|
5273
5369
|
return;
|
|
5274
|
-
const dir = dirname2(
|
|
5275
|
-
if (!
|
|
5370
|
+
const dir = dirname2(resolve3(filePath));
|
|
5371
|
+
if (!existsSync6(dir)) {
|
|
5276
5372
|
mkdirSync2(dir, { recursive: true });
|
|
5277
5373
|
}
|
|
5278
5374
|
}
|
|
@@ -5924,14 +6020,14 @@ var init_completion_guard = __esm(() => {
|
|
|
5924
6020
|
|
|
5925
6021
|
// src/lib/event-emission-safety.ts
|
|
5926
6022
|
import { tmpdir } from "os";
|
|
5927
|
-
import { resolve as
|
|
6023
|
+
import { resolve as resolve4, sep } from "path";
|
|
5928
6024
|
function envFlag(name) {
|
|
5929
6025
|
const value = process.env[name]?.trim().toLowerCase();
|
|
5930
6026
|
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
5931
6027
|
}
|
|
5932
6028
|
function isUnder(parent, child) {
|
|
5933
|
-
const normalizedParent =
|
|
5934
|
-
const normalizedChild =
|
|
6029
|
+
const normalizedParent = resolve4(parent);
|
|
6030
|
+
const normalizedChild = resolve4(child);
|
|
5935
6031
|
return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}${sep}`);
|
|
5936
6032
|
}
|
|
5937
6033
|
function databasePathFromDatabase(db) {
|
|
@@ -5974,9 +6070,9 @@ var init_event_emission_safety = __esm(() => {
|
|
|
5974
6070
|
});
|
|
5975
6071
|
|
|
5976
6072
|
// src/lib/workspace-trust.ts
|
|
5977
|
-
import { relative, resolve as
|
|
6073
|
+
import { relative, resolve as resolve5 } from "path";
|
|
5978
6074
|
function normalizePath(path) {
|
|
5979
|
-
return
|
|
6075
|
+
return resolve5(path);
|
|
5980
6076
|
}
|
|
5981
6077
|
function unique2(values) {
|
|
5982
6078
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -6158,9 +6254,9 @@ var init_workspace_trust = __esm(() => {
|
|
|
6158
6254
|
});
|
|
6159
6255
|
|
|
6160
6256
|
// src/lib/runner-sandbox.ts
|
|
6161
|
-
import { relative as relative2, resolve as
|
|
6257
|
+
import { relative as relative2, resolve as resolve6 } from "path";
|
|
6162
6258
|
function normalizePath2(path) {
|
|
6163
|
-
return
|
|
6259
|
+
return resolve6(path);
|
|
6164
6260
|
}
|
|
6165
6261
|
function unique3(values) {
|
|
6166
6262
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -6353,7 +6449,7 @@ var init_runner_sandbox = __esm(() => {
|
|
|
6353
6449
|
// src/lib/event-hooks.ts
|
|
6354
6450
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
|
|
6355
6451
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
6356
|
-
import { dirname as dirname3, resolve as
|
|
6452
|
+
import { dirname as dirname3, resolve as resolve7 } from "path";
|
|
6357
6453
|
import { createConnection } from "net";
|
|
6358
6454
|
function safeName(name) {
|
|
6359
6455
|
const trimmed = name.trim();
|
|
@@ -6491,7 +6587,7 @@ async function deliverHook(hook, envelope) {
|
|
|
6491
6587
|
if (hook.target === "stdout") {
|
|
6492
6588
|
output = line.trim();
|
|
6493
6589
|
} else if (hook.target === "file") {
|
|
6494
|
-
const filePath =
|
|
6590
|
+
const filePath = resolve7(hook.file_path);
|
|
6495
6591
|
mkdirSync3(dirname3(filePath), { recursive: true });
|
|
6496
6592
|
appendFileSync(filePath, line);
|
|
6497
6593
|
} else if (hook.target === "socket") {
|
|
@@ -6612,9 +6708,9 @@ var init_event_hooks = __esm(() => {
|
|
|
6612
6708
|
// node_modules/.bun/@hasna+events@0.1.16/node_modules/@hasna/events/dist/index.js
|
|
6613
6709
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
6614
6710
|
import { Buffer as Buffer2 } from "buffer";
|
|
6615
|
-
import { existsSync as
|
|
6616
|
-
import { homedir as
|
|
6617
|
-
import { join as
|
|
6711
|
+
import { existsSync as existsSync7 } from "fs";
|
|
6712
|
+
import { homedir as homedir4 } from "os";
|
|
6713
|
+
import { join as join6 } from "path";
|
|
6618
6714
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
6619
6715
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
6620
6716
|
import { spawn } from "child_process";
|
|
@@ -6715,7 +6811,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
6715
6811
|
return channel.filters.some((filter) => eventMatchesFilter(event, filter));
|
|
6716
6812
|
}
|
|
6717
6813
|
function getEventsDataDir(override) {
|
|
6718
|
-
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] ||
|
|
6814
|
+
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join6(homedir4(), ".hasna", "events");
|
|
6719
6815
|
}
|
|
6720
6816
|
|
|
6721
6817
|
class JsonEventsStore {
|
|
@@ -6724,12 +6820,12 @@ class JsonEventsStore {
|
|
|
6724
6820
|
channelsPath;
|
|
6725
6821
|
eventsPath;
|
|
6726
6822
|
deliveriesPath;
|
|
6727
|
-
constructor(
|
|
6728
|
-
this.dataDir =
|
|
6729
|
-
this.runtime = localJsonRuntime(
|
|
6730
|
-
this.channelsPath =
|
|
6731
|
-
this.eventsPath =
|
|
6732
|
-
this.deliveriesPath =
|
|
6823
|
+
constructor(dataDir2 = getEventsDataDir()) {
|
|
6824
|
+
this.dataDir = dataDir2;
|
|
6825
|
+
this.runtime = localJsonRuntime(dataDir2);
|
|
6826
|
+
this.channelsPath = join6(dataDir2, "channels.json");
|
|
6827
|
+
this.eventsPath = join6(dataDir2, "events.json");
|
|
6828
|
+
this.deliveriesPath = join6(dataDir2, "deliveries.json");
|
|
6733
6829
|
}
|
|
6734
6830
|
async init() {
|
|
6735
6831
|
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
@@ -6846,7 +6942,7 @@ class JsonEventsStore {
|
|
|
6846
6942
|
};
|
|
6847
6943
|
}
|
|
6848
6944
|
async ensureArrayFile(path) {
|
|
6849
|
-
if (!
|
|
6945
|
+
if (!existsSync7(path)) {
|
|
6850
6946
|
await writeFile(path, `[]
|
|
6851
6947
|
`, { encoding: "utf-8", mode: 384 });
|
|
6852
6948
|
}
|
|
@@ -6876,7 +6972,7 @@ class JsonEventsStore {
|
|
|
6876
6972
|
});
|
|
6877
6973
|
}
|
|
6878
6974
|
}
|
|
6879
|
-
function localJsonRuntime(
|
|
6975
|
+
function localJsonRuntime(dataDir2 = getEventsDataDir()) {
|
|
6880
6976
|
return {
|
|
6881
6977
|
mode: "local-files",
|
|
6882
6978
|
name: "json-events-store",
|
|
@@ -6889,7 +6985,7 @@ function localJsonRuntime(dataDir = getEventsDataDir()) {
|
|
|
6889
6985
|
durable: true,
|
|
6890
6986
|
idempotency: "best-effort-local",
|
|
6891
6987
|
replayCursors: true,
|
|
6892
|
-
description: `Local JSON files in ${
|
|
6988
|
+
description: `Local JSON files in ${dataDir2}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
|
|
6893
6989
|
};
|
|
6894
6990
|
}
|
|
6895
6991
|
function encodeLocalJsonEventCursor(offset, options = {}) {
|
|
@@ -7068,7 +7164,7 @@ async function dispatchCommand(event, channel) {
|
|
|
7068
7164
|
HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
|
|
7069
7165
|
HASNA_EVENT_JSON: eventJson
|
|
7070
7166
|
};
|
|
7071
|
-
return new Promise((
|
|
7167
|
+
return new Promise((resolve8) => {
|
|
7072
7168
|
const child = spawn(channel.command.command, channel.command.args ?? [], {
|
|
7073
7169
|
cwd: channel.command.cwd,
|
|
7074
7170
|
env,
|
|
@@ -7086,7 +7182,7 @@ async function dispatchCommand(event, channel) {
|
|
|
7086
7182
|
});
|
|
7087
7183
|
child.on("error", (error) => {
|
|
7088
7184
|
clearTimeout(timeout);
|
|
7089
|
-
|
|
7185
|
+
resolve8({
|
|
7090
7186
|
attempt: 1,
|
|
7091
7187
|
status: "failed",
|
|
7092
7188
|
startedAt,
|
|
@@ -7099,7 +7195,7 @@ async function dispatchCommand(event, channel) {
|
|
|
7099
7195
|
child.on("close", (code, signal) => {
|
|
7100
7196
|
clearTimeout(timeout);
|
|
7101
7197
|
const success = code === 0;
|
|
7102
|
-
|
|
7198
|
+
resolve8({
|
|
7103
7199
|
attempt: 1,
|
|
7104
7200
|
status: success ? "success" : "failed",
|
|
7105
7201
|
startedAt,
|
|
@@ -7441,7 +7537,7 @@ function normalizeRetryPolicy(policy) {
|
|
|
7441
7537
|
};
|
|
7442
7538
|
}
|
|
7443
7539
|
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;
|
|
7444
|
-
var
|
|
7540
|
+
var init_dist2 = __esm(() => {
|
|
7445
7541
|
DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
7446
7542
|
EventValidationError = class EventValidationError extends Error {
|
|
7447
7543
|
eventType;
|
|
@@ -7864,7 +7960,7 @@ function emitSharedTaskEventQuiet(input) {
|
|
|
7864
7960
|
}
|
|
7865
7961
|
var SOURCE = "todos";
|
|
7866
7962
|
var init_shared_events = __esm(() => {
|
|
7867
|
-
|
|
7963
|
+
init_dist2();
|
|
7868
7964
|
init_database();
|
|
7869
7965
|
init_projects();
|
|
7870
7966
|
init_task_lists();
|
|
@@ -9782,17 +9878,17 @@ function sanitizeCreateTaskInput(input) {
|
|
|
9782
9878
|
return {
|
|
9783
9879
|
...input,
|
|
9784
9880
|
title: sanitizePreWriteText(input.title, "task.title"),
|
|
9785
|
-
description: input.description
|
|
9881
|
+
description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
|
|
9786
9882
|
tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
|
|
9787
9883
|
metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined,
|
|
9788
|
-
reason: input.reason
|
|
9884
|
+
reason: input.reason == null ? input.reason : sanitizePreWriteText(input.reason, "task.reason")
|
|
9789
9885
|
};
|
|
9790
9886
|
}
|
|
9791
9887
|
function sanitizeUpdateTaskInput(input) {
|
|
9792
9888
|
return {
|
|
9793
9889
|
...input,
|
|
9794
9890
|
title: input.title !== undefined ? sanitizePreWriteText(input.title, "task.title") : undefined,
|
|
9795
|
-
description: input.description
|
|
9891
|
+
description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
|
|
9796
9892
|
tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
|
|
9797
9893
|
metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined
|
|
9798
9894
|
};
|
|
@@ -11646,28 +11742,28 @@ var init_boards = __esm(() => {
|
|
|
11646
11742
|
|
|
11647
11743
|
// src/lib/artifact-store.ts
|
|
11648
11744
|
import { createHash as createHash5 } from "crypto";
|
|
11649
|
-
import { existsSync as
|
|
11650
|
-
import { basename, dirname as dirname4, join as
|
|
11745
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
11746
|
+
import { basename, dirname as dirname4, join as join7, resolve as resolve8 } from "path";
|
|
11651
11747
|
import { tmpdir as tmpdir2 } from "os";
|
|
11652
11748
|
function isInMemoryDb2(path) {
|
|
11653
11749
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
11654
11750
|
}
|
|
11655
11751
|
function artifactStoreRoot() {
|
|
11656
11752
|
if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
|
|
11657
|
-
return
|
|
11753
|
+
return resolve8(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
|
|
11658
11754
|
if (process.env["TODOS_ARTIFACTS_DIR"])
|
|
11659
|
-
return
|
|
11755
|
+
return resolve8(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
11660
11756
|
const dbPath = getDatabasePath();
|
|
11661
11757
|
if (isInMemoryDb2(dbPath))
|
|
11662
|
-
return
|
|
11663
|
-
return
|
|
11758
|
+
return join7(tmpdir2(), "hasna-todos-artifacts");
|
|
11759
|
+
return join7(dirname4(resolve8(dbPath)), "artifacts");
|
|
11664
11760
|
}
|
|
11665
11761
|
function artifactStorePath(relativePath) {
|
|
11666
11762
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
11667
11763
|
if (normalized.includes("..") || normalized.startsWith("/") || normalized.length === 0) {
|
|
11668
11764
|
throw new Error("Invalid artifact store path");
|
|
11669
11765
|
}
|
|
11670
|
-
return
|
|
11766
|
+
return join7(artifactStoreRoot(), normalized);
|
|
11671
11767
|
}
|
|
11672
11768
|
function sha256(buffer) {
|
|
11673
11769
|
return createHash5("sha256").update(buffer).digest("hex");
|
|
@@ -11707,8 +11803,8 @@ function mediaTypeFor(path, textLike) {
|
|
|
11707
11803
|
return "application/octet-stream";
|
|
11708
11804
|
}
|
|
11709
11805
|
function storeArtifactContent(input) {
|
|
11710
|
-
const sourcePath =
|
|
11711
|
-
if (!
|
|
11806
|
+
const sourcePath = resolve8(input.path);
|
|
11807
|
+
if (!existsSync8(sourcePath))
|
|
11712
11808
|
return null;
|
|
11713
11809
|
const sourceStat = statSync2(sourcePath);
|
|
11714
11810
|
if (!sourceStat.isFile())
|
|
@@ -11725,9 +11821,9 @@ function storeArtifactContent(input) {
|
|
|
11725
11821
|
redactionStatus = "redacted";
|
|
11726
11822
|
}
|
|
11727
11823
|
const storedSha = sha256(storedBuffer);
|
|
11728
|
-
const relativePath =
|
|
11824
|
+
const relativePath = join7("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
|
|
11729
11825
|
const destination = artifactStorePath(relativePath);
|
|
11730
|
-
if (!
|
|
11826
|
+
if (!existsSync8(destination)) {
|
|
11731
11827
|
mkdirSync4(dirname4(destination), { recursive: true });
|
|
11732
11828
|
writeFileSync2(destination, storedBuffer);
|
|
11733
11829
|
}
|
|
@@ -11787,7 +11883,7 @@ function verifyStoredArtifact(input) {
|
|
|
11787
11883
|
};
|
|
11788
11884
|
}
|
|
11789
11885
|
const storedPath = artifactStorePath(store.relative_path);
|
|
11790
|
-
if (!
|
|
11886
|
+
if (!existsSync8(storedPath)) {
|
|
11791
11887
|
return {
|
|
11792
11888
|
id: input.id,
|
|
11793
11889
|
path: input.path,
|
|
@@ -11862,20 +11958,20 @@ function importStoredArtifactContent(content) {
|
|
|
11862
11958
|
}
|
|
11863
11959
|
function getArtifactStoreRoot(dbPath) {
|
|
11864
11960
|
if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
|
|
11865
|
-
return
|
|
11961
|
+
return resolve8(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
|
|
11866
11962
|
if (process.env["TODOS_ARTIFACTS_DIR"])
|
|
11867
|
-
return
|
|
11963
|
+
return resolve8(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
11868
11964
|
const path = dbPath ?? getDatabasePath();
|
|
11869
11965
|
if (isInMemoryDb2(path))
|
|
11870
|
-
return
|
|
11871
|
-
return
|
|
11966
|
+
return join7(tmpdir2(), "hasna-todos-artifacts");
|
|
11967
|
+
return join7(dirname4(resolve8(path)), "artifacts");
|
|
11872
11968
|
}
|
|
11873
11969
|
function computeContentHash(path) {
|
|
11874
|
-
return sha256(readFileSync4(
|
|
11970
|
+
return sha256(readFileSync4(resolve8(path)));
|
|
11875
11971
|
}
|
|
11876
11972
|
function storeArtifactFile(input) {
|
|
11877
|
-
const sourcePath =
|
|
11878
|
-
if (!
|
|
11973
|
+
const sourcePath = resolve8(input.sourcePath);
|
|
11974
|
+
if (!existsSync8(sourcePath)) {
|
|
11879
11975
|
throw new Error(`Source file not found: ${input.sourcePath}`);
|
|
11880
11976
|
}
|
|
11881
11977
|
if (!statSync2(sourcePath).isFile()) {
|
|
@@ -11888,7 +11984,7 @@ function storeArtifactFile(input) {
|
|
|
11888
11984
|
let localPath = sourcePath;
|
|
11889
11985
|
if (storageMode === "copy") {
|
|
11890
11986
|
const fileName = input.name && input.name.trim().length > 0 ? basename(input.name) : basename(sourcePath);
|
|
11891
|
-
const destination =
|
|
11987
|
+
const destination = join7(getArtifactStoreRoot(input.dbPath), input.artifactId, fileName);
|
|
11892
11988
|
mkdirSync4(dirname4(destination), { recursive: true });
|
|
11893
11989
|
writeFileSync2(destination, buffer);
|
|
11894
11990
|
localPath = destination;
|
|
@@ -11898,7 +11994,7 @@ function storeArtifactFile(input) {
|
|
|
11898
11994
|
function deleteStoredArtifactFile(localPath, storageMode, _dbPath2) {
|
|
11899
11995
|
if (storageMode === "reference")
|
|
11900
11996
|
return false;
|
|
11901
|
-
if (!localPath || !
|
|
11997
|
+
if (!localPath || !existsSync8(localPath))
|
|
11902
11998
|
return false;
|
|
11903
11999
|
rmSync(localPath, { force: true });
|
|
11904
12000
|
try {
|
|
@@ -11923,7 +12019,7 @@ function buildArtifactExportManifest(artifacts, dbPath) {
|
|
|
11923
12019
|
};
|
|
11924
12020
|
}
|
|
11925
12021
|
function writeArtifactExportManifest(manifest, outputPath) {
|
|
11926
|
-
const destination =
|
|
12022
|
+
const destination = resolve8(outputPath);
|
|
11927
12023
|
mkdirSync4(dirname4(destination), { recursive: true });
|
|
11928
12024
|
writeFileSync2(destination, `${JSON.stringify(manifest, null, 2)}
|
|
11929
12025
|
`);
|
|
@@ -13083,7 +13179,7 @@ import { createHash as createHash7 } from "crypto";
|
|
|
13083
13179
|
// package.json
|
|
13084
13180
|
var package_default = {
|
|
13085
13181
|
name: "@hasna/todos",
|
|
13086
|
-
version: "0.15.
|
|
13182
|
+
version: "0.15.51",
|
|
13087
13183
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
13088
13184
|
type: "module",
|
|
13089
13185
|
main: "dist/index.js",
|
|
@@ -13142,6 +13238,7 @@ var package_default = {
|
|
|
13142
13238
|
files: [
|
|
13143
13239
|
"dist",
|
|
13144
13240
|
"dashboard/dist",
|
|
13241
|
+
"postinstall.js",
|
|
13145
13242
|
"LICENSE",
|
|
13146
13243
|
"README.md"
|
|
13147
13244
|
],
|
|
@@ -13168,7 +13265,7 @@ var package_default = {
|
|
|
13168
13265
|
"test:attested-container-compatibility-vector": "bun test scripts/attested-container-compatibility-vector.test.ts",
|
|
13169
13266
|
"issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
|
|
13170
13267
|
prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
|
|
13171
|
-
postinstall: "
|
|
13268
|
+
postinstall: "node postinstall.js"
|
|
13172
13269
|
},
|
|
13173
13270
|
keywords: [
|
|
13174
13271
|
"todos",
|
|
@@ -13202,13 +13299,15 @@ var package_default = {
|
|
|
13202
13299
|
author: "Andrei Hasna <andrei@hasna.com>",
|
|
13203
13300
|
license: "Apache-2.0",
|
|
13204
13301
|
dependencies: {
|
|
13205
|
-
"@hasna/contracts": "0.14.
|
|
13302
|
+
"@hasna/contracts": "0.14.2",
|
|
13206
13303
|
"@hasna/events": "^0.1.11",
|
|
13304
|
+
"@hasna/paths": "0.1.0",
|
|
13207
13305
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
13208
13306
|
chalk: "^5.4.1",
|
|
13209
13307
|
commander: "^13.1.0",
|
|
13210
13308
|
ink: "^5.2.0",
|
|
13211
13309
|
react: "^18.3.1",
|
|
13310
|
+
"signal-exit": "3.0.7",
|
|
13212
13311
|
zod: "3.25.76"
|
|
13213
13312
|
},
|
|
13214
13313
|
overrides: {
|
|
@@ -16640,7 +16739,7 @@ async function retryOnTransientPostgresError(fn, attempts = 2, delayMs = 150) {
|
|
|
16640
16739
|
lastError = error;
|
|
16641
16740
|
if (!isTransientPostgresError(error) || attempt === attempts)
|
|
16642
16741
|
throw error;
|
|
16643
|
-
await new Promise((
|
|
16742
|
+
await new Promise((resolve2) => setTimeout(resolve2, delayMs * attempt));
|
|
16644
16743
|
}
|
|
16645
16744
|
}
|
|
16646
16745
|
throw lastError;
|
|
@@ -19494,8 +19593,8 @@ class SqliteTodosProjectRegistrationBackend {
|
|
|
19494
19593
|
async transaction(fn) {
|
|
19495
19594
|
const previous = sqliteTransactionTails.get(this.db) ?? Promise.resolve();
|
|
19496
19595
|
let release;
|
|
19497
|
-
const current = new Promise((
|
|
19498
|
-
release =
|
|
19596
|
+
const current = new Promise((resolve9) => {
|
|
19597
|
+
release = resolve9;
|
|
19499
19598
|
});
|
|
19500
19599
|
sqliteTransactionTails.set(this.db, current);
|
|
19501
19600
|
await previous;
|