things-api 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/bin/things.js +9 -0
- package/dist/audit/log.d.ts +10 -0
- package/dist/audit/log.js +31 -0
- package/dist/audit/log.js.map +1 -0
- package/dist/audit/schema.d.ts +37 -0
- package/dist/audit/schema.js +9 -0
- package/dist/audit/schema.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +9 -0
- package/dist/cli/commands/doctor.js +60 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +7 -0
- package/dist/cli/commands/mcp.js +26 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/project.d.ts +5 -0
- package/dist/cli/commands/project.js +50 -0
- package/dist/cli/commands/project.js.map +1 -0
- package/dist/cli/commands/reads.d.ts +21 -0
- package/dist/cli/commands/reads.js +295 -0
- package/dist/cli/commands/reads.js.map +1 -0
- package/dist/cli/commands/snapshot.d.ts +7 -0
- package/dist/cli/commands/snapshot.js +22 -0
- package/dist/cli/commands/snapshot.js.map +1 -0
- package/dist/cli/commands/todo.d.ts +6 -0
- package/dist/cli/commands/todo.js +43 -0
- package/dist/cli/commands/todo.js.map +1 -0
- package/dist/cli/commands/writes.d.ts +9 -0
- package/dist/cli/commands/writes.js +994 -0
- package/dist/cli/commands/writes.js.map +1 -0
- package/dist/cli/exit-codes.d.ts +26 -0
- package/dist/cli/exit-codes.js +26 -0
- package/dist/cli/exit-codes.js.map +1 -0
- package/dist/cli/main.d.ts +4 -0
- package/dist/cli/main.js +73 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.d.ts +42 -0
- package/dist/cli/output.js +16 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/style.d.ts +6 -0
- package/dist/cli/style.js +25 -0
- package/dist/cli/style.js.map +1 -0
- package/dist/client.d.ts +184 -0
- package/dist/client.js +187 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.js +76 -0
- package/dist/config.js.map +1 -0
- package/dist/contracts.d.ts +79 -0
- package/dist/contracts.js +46 -0
- package/dist/contracts.js.map +1 -0
- package/dist/db/baselines/db-v26.d.ts +8 -0
- package/dist/db/baselines/db-v26.js +16 -0
- package/dist/db/baselines/db-v26.js.map +1 -0
- package/dist/db/baselines/index.d.ts +3 -0
- package/dist/db/baselines/index.js +4 -0
- package/dist/db/baselines/index.js.map +1 -0
- package/dist/db/connection.d.ts +22 -0
- package/dist/db/connection.js +40 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/fingerprint.d.ts +42 -0
- package/dist/db/fingerprint.js +82 -0
- package/dist/db/fingerprint.js.map +1 -0
- package/dist/db/locate.d.ts +13 -0
- package/dist/db/locate.js +44 -0
- package/dist/db/locate.js.map +1 -0
- package/dist/db/schema.d.ts +37 -0
- package/dist/db/schema.js +81 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/diagnose.d.ts +67 -0
- package/dist/diagnose.js +143 -0
- package/dist/diagnose.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +1002 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/model/dates.d.ts +38 -0
- package/dist/model/dates.js +104 -0
- package/dist/model/dates.js.map +1 -0
- package/dist/model/entities.d.ts +117 -0
- package/dist/model/entities.js +21 -0
- package/dist/model/entities.js.map +1 -0
- package/dist/model/mappers.d.ts +55 -0
- package/dist/model/mappers.js +105 -0
- package/dist/model/mappers.js.map +1 -0
- package/dist/model/occurrences.d.ts +46 -0
- package/dist/model/occurrences.js +164 -0
- package/dist/model/occurrences.js.map +1 -0
- package/dist/model/recurrence.d.ts +52 -0
- package/dist/model/recurrence.js +164 -0
- package/dist/model/recurrence.js.map +1 -0
- package/dist/paths.d.ts +6 -0
- package/dist/paths.js +30 -0
- package/dist/paths.js.map +1 -0
- package/dist/read/detail.d.ts +8 -0
- package/dist/read/detail.js +34 -0
- package/dist/read/detail.js.map +1 -0
- package/dist/read/project-view.d.ts +34 -0
- package/dist/read/project-view.js +87 -0
- package/dist/read/project-view.js.map +1 -0
- package/dist/read/queries.d.ts +39 -0
- package/dist/read/queries.js +134 -0
- package/dist/read/queries.js.map +1 -0
- package/dist/read/snapshot.d.ts +25 -0
- package/dist/read/snapshot.js +39 -0
- package/dist/read/snapshot.js.map +1 -0
- package/dist/read/tags.d.ts +17 -0
- package/dist/read/tags.js +64 -0
- package/dist/read/tags.js.map +1 -0
- package/dist/read/views.d.ts +152 -0
- package/dist/read/views.js +322 -0
- package/dist/read/views.js.map +1 -0
- package/dist/surface-copy.d.ts +15 -0
- package/dist/surface-copy.js +16 -0
- package/dist/surface-copy.js.map +1 -0
- package/dist/write/automation-probe.d.ts +12 -0
- package/dist/write/automation-probe.js +59 -0
- package/dist/write/automation-probe.js.map +1 -0
- package/dist/write/batch.d.ts +51 -0
- package/dist/write/batch.js +97 -0
- package/dist/write/batch.js.map +1 -0
- package/dist/write/capabilities.d.ts +16 -0
- package/dist/write/capabilities.js +16 -0
- package/dist/write/capabilities.js.map +1 -0
- package/dist/write/commands.d.ts +32 -0
- package/dist/write/commands.js +1274 -0
- package/dist/write/commands.js.map +1 -0
- package/dist/write/environment.d.ts +29 -0
- package/dist/write/environment.js +105 -0
- package/dist/write/environment.js.map +1 -0
- package/dist/write/experimental.d.ts +7 -0
- package/dist/write/experimental.js +39 -0
- package/dist/write/experimental.js.map +1 -0
- package/dist/write/failure-hints.d.ts +32 -0
- package/dist/write/failure-hints.js +71 -0
- package/dist/write/failure-hints.js.map +1 -0
- package/dist/write/guards.d.ts +21 -0
- package/dist/write/guards.js +312 -0
- package/dist/write/guards.js.map +1 -0
- package/dist/write/lock.d.ts +10 -0
- package/dist/write/lock.js +70 -0
- package/dist/write/lock.js.map +1 -0
- package/dist/write/operations.d.ts +262 -0
- package/dist/write/operations.js +34 -0
- package/dist/write/operations.js.map +1 -0
- package/dist/write/pipeline.d.ts +90 -0
- package/dist/write/pipeline.js +375 -0
- package/dist/write/pipeline.js.map +1 -0
- package/dist/write/planner.d.ts +40 -0
- package/dist/write/planner.js +57 -0
- package/dist/write/planner.js.map +1 -0
- package/dist/write/pre-state.d.ts +125 -0
- package/dist/write/pre-state.js +299 -0
- package/dist/write/pre-state.js.map +1 -0
- package/dist/write/reopen.d.ts +29 -0
- package/dist/write/reopen.js +63 -0
- package/dist/write/reopen.js.map +1 -0
- package/dist/write/reorder.d.ts +19 -0
- package/dist/write/reorder.js +285 -0
- package/dist/write/reorder.js.map +1 -0
- package/dist/write/undo.d.ts +53 -0
- package/dist/write/undo.js +703 -0
- package/dist/write/undo.js.map +1 -0
- package/dist/write/vectors/applescript.d.ts +5 -0
- package/dist/write/vectors/applescript.js +216 -0
- package/dist/write/vectors/applescript.js.map +1 -0
- package/dist/write/vectors/registry.d.ts +2 -0
- package/dist/write/vectors/registry.js +11 -0
- package/dist/write/vectors/registry.js.map +1 -0
- package/dist/write/vectors/types.d.ts +48 -0
- package/dist/write/vectors/types.js +2 -0
- package/dist/write/vectors/types.js.map +1 -0
- package/dist/write/vectors/url-scheme.d.ts +3 -0
- package/dist/write/vectors/url-scheme.js +176 -0
- package/dist/write/vectors/url-scheme.js.map +1 -0
- package/dist/write/verify/delta.d.ts +126 -0
- package/dist/write/verify/delta.js +281 -0
- package/dist/write/verify/delta.js.map +1 -0
- package/dist/write/verify/poller.d.ts +24 -0
- package/dist/write/verify/poller.js +28 -0
- package/dist/write/verify/poller.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema fingerprinting for drift detection.
|
|
3
|
+
*
|
|
4
|
+
* The fingerprint is SHA-256 over a canonical JSON structure derived from
|
|
5
|
+
* PRAGMA table_info for exactly the tables/columns in the dependency
|
|
6
|
+
* manifest (src/db/schema.ts). Extra columns are recorded (warn-only);
|
|
7
|
+
* missing tables/columns change the hash (drift → writes blocked).
|
|
8
|
+
* Enum domains are runtime probes, not part of the hash.
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import { DEPENDED_TABLES, q, TABLE_NAMES } from "./schema.js";
|
|
12
|
+
export function observeSchema(db) {
|
|
13
|
+
const tables = TABLE_NAMES.map((table) => {
|
|
14
|
+
const rows = db.prepare(`PRAGMA table_info(${q(table)})`).all();
|
|
15
|
+
const byName = new Map(rows.map((r) => [r.name, r]));
|
|
16
|
+
const declared = DEPENDED_TABLES[table];
|
|
17
|
+
const columns = declared.map((name) => {
|
|
18
|
+
const row = byName.get(name);
|
|
19
|
+
if (!row)
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
name: row.name,
|
|
23
|
+
declaredType: row.type.toUpperCase(),
|
|
24
|
+
notnull: row.notnull ? 1 : 0,
|
|
25
|
+
pk: row.pk,
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
const declaredSet = new Set(declared);
|
|
29
|
+
const extraColumns = rows
|
|
30
|
+
.map((r) => r.name)
|
|
31
|
+
.filter((name) => !declaredSet.has(name))
|
|
32
|
+
.sort();
|
|
33
|
+
return { table, present: rows.length > 0, columns, extraColumns };
|
|
34
|
+
});
|
|
35
|
+
const canonical = tables.map((t) => ({
|
|
36
|
+
table: t.table,
|
|
37
|
+
present: t.present,
|
|
38
|
+
columns: t.columns,
|
|
39
|
+
// extraColumns intentionally excluded from the hash: additions warn, not block
|
|
40
|
+
}));
|
|
41
|
+
const fingerprint = `sha256:${createHash("sha256")
|
|
42
|
+
.update(JSON.stringify(canonical))
|
|
43
|
+
.digest("hex")}`;
|
|
44
|
+
return { databaseVersion: readDatabaseVersion(db), tables, fingerprint };
|
|
45
|
+
}
|
|
46
|
+
/** Meta.databaseVersion is a plist blob/text wrapping an <integer>. */
|
|
47
|
+
export function readDatabaseVersion(db) {
|
|
48
|
+
try {
|
|
49
|
+
const row = db.prepare(`SELECT value FROM Meta WHERE key = 'databaseVersion'`).get();
|
|
50
|
+
if (!row || row.value == null)
|
|
51
|
+
return null;
|
|
52
|
+
const text = typeof row.value === "string" ? row.value : new TextDecoder().decode(row.value);
|
|
53
|
+
const match = /<integer>(\d+)<\/integer>/.exec(text);
|
|
54
|
+
if (match?.[1])
|
|
55
|
+
return Number(match[1]);
|
|
56
|
+
const bare = /^\s*(\d+)\s*$/.exec(text);
|
|
57
|
+
return bare?.[1] ? Number(bare[1]) : null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function compareToBaseline(observation, baselines) {
|
|
64
|
+
const baseline = baselines.find((b) => b.databaseVersion === observation.databaseVersion);
|
|
65
|
+
if (!baseline)
|
|
66
|
+
return { kind: "unknown-version", observation };
|
|
67
|
+
if (baseline.fingerprint === observation.fingerprint)
|
|
68
|
+
return { kind: "ok", observation };
|
|
69
|
+
const detail = observation.tables.flatMap((t) => {
|
|
70
|
+
if (!t.present)
|
|
71
|
+
return [`table missing: ${t.table}`];
|
|
72
|
+
const declared = DEPENDED_TABLES[t.table];
|
|
73
|
+
const lines = [];
|
|
74
|
+
t.columns.forEach((c, i) => {
|
|
75
|
+
if (c === null)
|
|
76
|
+
lines.push(`column missing: ${t.table}.${declared[i] ?? `#${i}`}`);
|
|
77
|
+
});
|
|
78
|
+
return lines;
|
|
79
|
+
});
|
|
80
|
+
return { kind: "drift", observation, expected: baseline.fingerprint, detail };
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=fingerprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/db/fingerprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface LocateResult {
|
|
2
|
+
path: string;
|
|
3
|
+
source: "option" | "env" | "container";
|
|
4
|
+
/** Additional candidate paths that also matched (warn when >1). */
|
|
5
|
+
otherCandidates: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare class ThingsDbNotFoundError extends Error {
|
|
8
|
+
constructor(searched: string);
|
|
9
|
+
}
|
|
10
|
+
export declare function locateThingsDb(options?: {
|
|
11
|
+
dbPath?: string;
|
|
12
|
+
home?: string;
|
|
13
|
+
}): LocateResult;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locate the Things database. Precedence:
|
|
3
|
+
* 1. explicit dbPath option
|
|
4
|
+
* 2. THINGS_DB environment variable
|
|
5
|
+
* 3. glob under the Things group container (most recently modified wins)
|
|
6
|
+
*/
|
|
7
|
+
import { globSync, statSync } from "node:fs";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
const CONTAINER_GLOB = join("Library/Group Containers/JLMPQHK86H.com.culturedcode.ThingsMac", "ThingsData-*/Things Database.thingsdatabase/main.sqlite");
|
|
11
|
+
export class ThingsDbNotFoundError extends Error {
|
|
12
|
+
constructor(searched) {
|
|
13
|
+
super(`Things database not found (searched ${searched}). ` +
|
|
14
|
+
`Is Things installed and has it been launched at least once? ` +
|
|
15
|
+
`Override with THINGS_DB or openThings({ dbPath }).`);
|
|
16
|
+
this.name = "ThingsDbNotFoundError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function locateThingsDb(options) {
|
|
20
|
+
if (options?.dbPath) {
|
|
21
|
+
return { path: options.dbPath, source: "option", otherCandidates: [] };
|
|
22
|
+
}
|
|
23
|
+
const envPath = process.env["THINGS_DB"];
|
|
24
|
+
if (envPath) {
|
|
25
|
+
return { path: envPath, source: "env", otherCandidates: [] };
|
|
26
|
+
}
|
|
27
|
+
const home = options?.home ?? homedir();
|
|
28
|
+
const matches = globSync(CONTAINER_GLOB, { cwd: home }).map((rel) => join(home, rel));
|
|
29
|
+
if (matches.length === 0) {
|
|
30
|
+
throw new ThingsDbNotFoundError(join(home, CONTAINER_GLOB));
|
|
31
|
+
}
|
|
32
|
+
const byMtime = matches
|
|
33
|
+
.map((path) => ({ path, mtime: statSync(path).mtimeMs }))
|
|
34
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
35
|
+
const first = byMtime[0];
|
|
36
|
+
if (!first)
|
|
37
|
+
throw new ThingsDbNotFoundError(join(home, CONTAINER_GLOB));
|
|
38
|
+
return {
|
|
39
|
+
path: first.path,
|
|
40
|
+
source: "container",
|
|
41
|
+
otherCandidates: byMtime.slice(1).map((entry) => entry.path),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=locate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locate.js","sourceRoot":"","sources":["../../src/db/locate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,cAAc,GAAG,IAAI,CACzB,gEAAgE,EAChE,yDAAyD,CAC1D,CAAC;AASF,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,QAAgB;QAC1B,KAAK,CACH,uCAAuC,QAAQ,KAAK;YAClD,8DAA8D;YAC9D,oDAAoD,CACvD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,OAA4C;IACzE,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC;IACzE,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACzC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC;IAC/D,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACtF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IAC9D,CAAC;IACD,MAAM,OAAO,GAAG,OAAO;SACpB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;SACxD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IACxE,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,WAAW;QACnB,eAAe,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;KAC7D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SINGLE SOURCE OF TRUTH for every table and column things-api depends on.
|
|
3
|
+
*
|
|
4
|
+
* - Read queries name columns exclusively from this manifest (loud failure
|
|
5
|
+
* when a column disappears).
|
|
6
|
+
* - The schema fingerprint (./fingerprint.ts) hashes exactly this structure
|
|
7
|
+
* as observed via PRAGMA table_info.
|
|
8
|
+
* - docs/atlas/schema-v26.md documents the semantics of every entry.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEPENDED_TABLES: {
|
|
11
|
+
readonly TMTask: readonly ["uuid", "type", "status", "stopDate", "trashed", "title", "notes", "creationDate", "userModificationDate", "start", "startDate", "startBucket", "reminderTime", "deadline", "deadlineSuppressionDate", "index", "todayIndex", "todayIndexReferenceDate", "area", "project", "heading", "untrashedLeafActionsCount", "openUntrashedLeafActionsCount", "checklistItemsCount", "openChecklistItemsCount", "rt1_repeatingTemplate", "rt1_recurrenceRule", "rt1_nextInstanceStartDate", "rt1_instanceCreationPaused", "repeater"];
|
|
12
|
+
readonly TMArea: readonly ["uuid", "title", "visible", "index"];
|
|
13
|
+
readonly TMTag: readonly ["uuid", "title", "shortcut", "usedDate", "parent", "index"];
|
|
14
|
+
readonly TMTaskTag: readonly ["tasks", "tags"];
|
|
15
|
+
readonly TMAreaTag: readonly ["areas", "tags"];
|
|
16
|
+
readonly TMChecklistItem: readonly ["uuid", "title", "status", "stopDate", "index", "task", "creationDate", "userModificationDate"];
|
|
17
|
+
readonly TMSettings: readonly ["uuid", "uriSchemeAuthenticationToken", "groupTodayByParent"];
|
|
18
|
+
readonly Meta: readonly ["key", "value"];
|
|
19
|
+
};
|
|
20
|
+
export type DependedTable = keyof typeof DEPENDED_TABLES;
|
|
21
|
+
export declare const TABLE_NAMES: DependedTable[];
|
|
22
|
+
/**
|
|
23
|
+
* Enum domains checked as runtime probes (not part of the structural hash;
|
|
24
|
+
* out-of-domain values raise drift warnings). Verified live 2026-07-02.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ENUM_DOMAINS: {
|
|
27
|
+
readonly "TMTask.type": readonly [0, 1, 2];
|
|
28
|
+
readonly "TMTask.status": readonly [0, 2, 3];
|
|
29
|
+
readonly "TMTask.start": readonly [0, 1, 2];
|
|
30
|
+
readonly "TMTask.startBucket": readonly [0, 1];
|
|
31
|
+
readonly "TMTask.trashed": readonly [0, 1];
|
|
32
|
+
readonly "TMChecklistItem.status": readonly [0, 2, 3];
|
|
33
|
+
};
|
|
34
|
+
/** Quote an identifier for SQL ("index" is a keyword and a real column name). */
|
|
35
|
+
export declare function q(identifier: string): string;
|
|
36
|
+
/** Explicit select list for a depended table (never `SELECT *`). */
|
|
37
|
+
export declare function selectList(table: DependedTable): string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SINGLE SOURCE OF TRUTH for every table and column things-api depends on.
|
|
3
|
+
*
|
|
4
|
+
* - Read queries name columns exclusively from this manifest (loud failure
|
|
5
|
+
* when a column disappears).
|
|
6
|
+
* - The schema fingerprint (./fingerprint.ts) hashes exactly this structure
|
|
7
|
+
* as observed via PRAGMA table_info.
|
|
8
|
+
* - docs/atlas/schema-v26.md documents the semantics of every entry.
|
|
9
|
+
*/
|
|
10
|
+
export const DEPENDED_TABLES = {
|
|
11
|
+
TMTask: [
|
|
12
|
+
"uuid",
|
|
13
|
+
"type",
|
|
14
|
+
"status",
|
|
15
|
+
"stopDate",
|
|
16
|
+
"trashed",
|
|
17
|
+
"title",
|
|
18
|
+
"notes",
|
|
19
|
+
"creationDate",
|
|
20
|
+
"userModificationDate",
|
|
21
|
+
"start",
|
|
22
|
+
"startDate",
|
|
23
|
+
"startBucket",
|
|
24
|
+
"reminderTime",
|
|
25
|
+
"deadline",
|
|
26
|
+
"deadlineSuppressionDate",
|
|
27
|
+
"index",
|
|
28
|
+
"todayIndex",
|
|
29
|
+
"todayIndexReferenceDate",
|
|
30
|
+
"area",
|
|
31
|
+
"project",
|
|
32
|
+
"heading",
|
|
33
|
+
"untrashedLeafActionsCount",
|
|
34
|
+
"openUntrashedLeafActionsCount",
|
|
35
|
+
"checklistItemsCount",
|
|
36
|
+
"openChecklistItemsCount",
|
|
37
|
+
"rt1_repeatingTemplate",
|
|
38
|
+
"rt1_recurrenceRule",
|
|
39
|
+
"rt1_nextInstanceStartDate",
|
|
40
|
+
"rt1_instanceCreationPaused",
|
|
41
|
+
"repeater",
|
|
42
|
+
],
|
|
43
|
+
TMArea: ["uuid", "title", "visible", "index"],
|
|
44
|
+
TMTag: ["uuid", "title", "shortcut", "usedDate", "parent", "index"],
|
|
45
|
+
TMTaskTag: ["tasks", "tags"],
|
|
46
|
+
TMAreaTag: ["areas", "tags"],
|
|
47
|
+
TMChecklistItem: [
|
|
48
|
+
"uuid",
|
|
49
|
+
"title",
|
|
50
|
+
"status",
|
|
51
|
+
"stopDate",
|
|
52
|
+
"index",
|
|
53
|
+
"task",
|
|
54
|
+
"creationDate",
|
|
55
|
+
"userModificationDate",
|
|
56
|
+
],
|
|
57
|
+
TMSettings: ["uuid", "uriSchemeAuthenticationToken", "groupTodayByParent"],
|
|
58
|
+
Meta: ["key", "value"],
|
|
59
|
+
};
|
|
60
|
+
export const TABLE_NAMES = Object.keys(DEPENDED_TABLES);
|
|
61
|
+
/**
|
|
62
|
+
* Enum domains checked as runtime probes (not part of the structural hash;
|
|
63
|
+
* out-of-domain values raise drift warnings). Verified live 2026-07-02.
|
|
64
|
+
*/
|
|
65
|
+
export const ENUM_DOMAINS = {
|
|
66
|
+
"TMTask.type": [0, 1, 2], // to-do | project | heading
|
|
67
|
+
"TMTask.status": [0, 2, 3], // open | canceled | completed
|
|
68
|
+
"TMTask.start": [0, 1, 2], // inbox | active | someday
|
|
69
|
+
"TMTask.startBucket": [0, 1], // today | evening
|
|
70
|
+
"TMTask.trashed": [0, 1],
|
|
71
|
+
"TMChecklistItem.status": [0, 2, 3],
|
|
72
|
+
};
|
|
73
|
+
/** Quote an identifier for SQL ("index" is a keyword and a real column name). */
|
|
74
|
+
export function q(identifier) {
|
|
75
|
+
return `"${identifier.replaceAll('"', '""')}"`;
|
|
76
|
+
}
|
|
77
|
+
/** Explicit select list for a depended table (never `SELECT *`). */
|
|
78
|
+
export function selectList(table) {
|
|
79
|
+
return DEPENDED_TABLES[table].map(q).join(", ");
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM,EAAE;QACN,MAAM;QACN,MAAM;QACN,QAAQ;QACR,UAAU;QACV,SAAS;QACT,OAAO;QACP,OAAO;QACP,cAAc;QACd,sBAAsB;QACtB,OAAO;QACP,WAAW;QACX,aAAa;QACb,cAAc;QACd,UAAU;QACV,yBAAyB;QACzB,OAAO;QACP,YAAY;QACZ,yBAAyB;QACzB,MAAM;QACN,SAAS;QACT,SAAS;QACT,2BAA2B;QAC3B,+BAA+B;QAC/B,qBAAqB;QACrB,yBAAyB;QACzB,uBAAuB;QACvB,oBAAoB;QACpB,2BAA2B;QAC3B,4BAA4B;QAC5B,UAAU;KACX;IACD,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC;IAC7C,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC;IACnE,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE;QACf,MAAM;QACN,OAAO;QACP,QAAQ;QACR,UAAU;QACV,OAAO;QACP,MAAM;QACN,cAAc;QACd,sBAAsB;KACvB;IACD,UAAU,EAAE,CAAC,MAAM,EAAE,8BAA8B,EAAE,oBAAoB,CAAC;IAC1E,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;CACd,CAAC;AAIX,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAoB,CAAC;AAE3E;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,aAAa,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,4BAA4B;IACtD,eAAe,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,8BAA8B;IAC1D,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,2BAA2B;IACtD,oBAAoB,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,kBAAkB;IAChD,gBAAgB,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACxB,wBAAwB,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;CAC3B,CAAC;AAEX,iFAAiF;AACjF,MAAM,UAAU,CAAC,CAAC,UAAkB;IAClC,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;AACjD,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,CAAC,KAAoB;IAC7C,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type AutomationProbeDeps, type AutomationProbeStatus } from "./write/automation-probe.ts";
|
|
2
|
+
import { type EnvironmentChange, type EnvironmentTracker, type EnvironmentTuple } from "./write/environment.ts";
|
|
3
|
+
import { ExitCode, type EnvelopeMeta } from "./contracts.ts";
|
|
4
|
+
export interface DiagnoseReport {
|
|
5
|
+
db: {
|
|
6
|
+
path: string;
|
|
7
|
+
source: "option" | "env" | "container";
|
|
8
|
+
otherCandidates: string[];
|
|
9
|
+
databaseVersion: number | null;
|
|
10
|
+
};
|
|
11
|
+
fingerprint: {
|
|
12
|
+
status: "ok" | "drift" | "user-accepted" | "unknown-version";
|
|
13
|
+
value: string;
|
|
14
|
+
expected: string | null;
|
|
15
|
+
detail: string[];
|
|
16
|
+
extraColumns: Record<string, string[]>;
|
|
17
|
+
};
|
|
18
|
+
app: {
|
|
19
|
+
installed: boolean;
|
|
20
|
+
};
|
|
21
|
+
writes: {
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
reason: string;
|
|
24
|
+
};
|
|
25
|
+
experimental: {
|
|
26
|
+
/** config allowExperimental (opt-in for private-surface capabilities). */
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
/** sdef canary: the private reorder command is still declared. */
|
|
29
|
+
sdefDeclaresReorder: boolean;
|
|
30
|
+
reason: string;
|
|
31
|
+
};
|
|
32
|
+
environment: {
|
|
33
|
+
/** The identity tuple macOS consent grants key on (docs/setup.md, hardening). */
|
|
34
|
+
current: EnvironmentTuple;
|
|
35
|
+
/** Tuple recorded at the last verified mutation; null before the first one. */
|
|
36
|
+
lastVerifiedWrite: EnvironmentTuple | null;
|
|
37
|
+
/** Non-empty = re-consent risk: something changed since the last verified write. */
|
|
38
|
+
changes: EnvironmentChange[];
|
|
39
|
+
};
|
|
40
|
+
automation: {
|
|
41
|
+
status: AutomationProbeStatus | "not-probed";
|
|
42
|
+
detail: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export interface DiagnoseOptions {
|
|
46
|
+
/**
|
|
47
|
+
* Actively test Automation consent by querying Things once. Opt-in: on an
|
|
48
|
+
* unauthorized machine the probe makes macOS show the consent prompt
|
|
49
|
+
* (useful during onboarding, unwanted headless). Skipped when Things is
|
|
50
|
+
* not running so a diagnostic never launches the app.
|
|
51
|
+
*/
|
|
52
|
+
probeAutomation?: boolean;
|
|
53
|
+
/** Test seams. */
|
|
54
|
+
probeDeps?: AutomationProbeDeps;
|
|
55
|
+
environment?: EnvironmentTracker;
|
|
56
|
+
}
|
|
57
|
+
export interface DiagnoseResult {
|
|
58
|
+
report: DiagnoseReport | null;
|
|
59
|
+
error: {
|
|
60
|
+
code: string;
|
|
61
|
+
message: string;
|
|
62
|
+
remediation?: string;
|
|
63
|
+
} | null;
|
|
64
|
+
exitCode: ExitCode;
|
|
65
|
+
meta: Pick<EnvelopeMeta, "dbVersion" | "fingerprint">;
|
|
66
|
+
}
|
|
67
|
+
export declare function diagnose(dbPath?: string, options?: DiagnoseOptions): DiagnoseResult;
|
package/dist/diagnose.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment/database/fingerprint health report — the library function
|
|
3
|
+
* behind `things doctor` and the MCP doctor tool. Read-only: opens the DB,
|
|
4
|
+
* observes the schema, checks the app bundle and the experimental canary.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { loadConfig } from "./config.js";
|
|
8
|
+
import { BASELINES } from "./db/baselines/index.js";
|
|
9
|
+
import { openConnection, ThingsDbOpenError } from "./db/connection.js";
|
|
10
|
+
import { compareToBaseline, observeSchema } from "./db/fingerprint.js";
|
|
11
|
+
import { locateThingsDb, ThingsDbNotFoundError } from "./db/locate.js";
|
|
12
|
+
import { probeAutomation, } from "./write/automation-probe.js";
|
|
13
|
+
import { createEnvironmentTracker, diffEnvironment, } from "./write/environment.js";
|
|
14
|
+
import { sdefDeclaresPrivateReorder } from "./write/experimental.js";
|
|
15
|
+
import { ExitCode, PKG_VERSION } from "./contracts.js";
|
|
16
|
+
const THINGS_APP = "/Applications/Things3.app";
|
|
17
|
+
export function diagnose(dbPath, options = {}) {
|
|
18
|
+
let located;
|
|
19
|
+
try {
|
|
20
|
+
located = locateThingsDb(dbPath ? { dbPath } : undefined);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
if (err instanceof ThingsDbNotFoundError) {
|
|
24
|
+
return {
|
|
25
|
+
report: null,
|
|
26
|
+
error: {
|
|
27
|
+
code: "environment",
|
|
28
|
+
message: err.message,
|
|
29
|
+
remediation: "Install Things 3 and launch it once, or set THINGS_DB.",
|
|
30
|
+
},
|
|
31
|
+
exitCode: ExitCode.Environment,
|
|
32
|
+
meta: { dbVersion: null, fingerprint: "unknown" },
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
let conn;
|
|
38
|
+
try {
|
|
39
|
+
conn = openConnection(located.path);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
if (err instanceof ThingsDbOpenError) {
|
|
43
|
+
return {
|
|
44
|
+
report: null,
|
|
45
|
+
error: {
|
|
46
|
+
code: "environment",
|
|
47
|
+
message: err.message,
|
|
48
|
+
remediation: "Launch Things once so the WAL sidecars exist, then retry.",
|
|
49
|
+
},
|
|
50
|
+
exitCode: ExitCode.Environment,
|
|
51
|
+
meta: { dbVersion: null, fingerprint: "unknown" },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const observation = observeSchema(conn.db);
|
|
58
|
+
const status = compareToBaseline(observation, BASELINES);
|
|
59
|
+
// The pipeline honors a user-accepted drifted fingerprint (loud escape
|
|
60
|
+
// hatch, design §6) — doctor must report what will actually happen.
|
|
61
|
+
const config = loadConfig();
|
|
62
|
+
const accepted = status.kind === "drift" && config.acceptedFingerprint === observation.fingerprint;
|
|
63
|
+
const fingerprintStatus = accepted
|
|
64
|
+
? "user-accepted"
|
|
65
|
+
: status.kind === "ok"
|
|
66
|
+
? "ok"
|
|
67
|
+
: status.kind === "drift"
|
|
68
|
+
? "drift"
|
|
69
|
+
: "unknown-version";
|
|
70
|
+
const writesEnabled = status.kind === "ok" || accepted;
|
|
71
|
+
const sdefCanary = sdefDeclaresPrivateReorder();
|
|
72
|
+
const tracker = options.environment ?? createEnvironmentTracker(PKG_VERSION);
|
|
73
|
+
const currentEnv = tracker.capture();
|
|
74
|
+
const recordedEnv = tracker.load();
|
|
75
|
+
const extraColumns = {};
|
|
76
|
+
for (const t of observation.tables) {
|
|
77
|
+
if (t.extraColumns.length > 0)
|
|
78
|
+
extraColumns[t.table] = t.extraColumns;
|
|
79
|
+
}
|
|
80
|
+
const report = {
|
|
81
|
+
db: {
|
|
82
|
+
path: located.path,
|
|
83
|
+
source: located.source,
|
|
84
|
+
otherCandidates: located.otherCandidates,
|
|
85
|
+
databaseVersion: observation.databaseVersion,
|
|
86
|
+
},
|
|
87
|
+
fingerprint: {
|
|
88
|
+
status: fingerprintStatus,
|
|
89
|
+
value: observation.fingerprint,
|
|
90
|
+
expected: status.kind === "drift" ? status.expected : null,
|
|
91
|
+
detail: status.kind === "drift" ? status.detail : [],
|
|
92
|
+
extraColumns,
|
|
93
|
+
},
|
|
94
|
+
app: { installed: existsSync(THINGS_APP) },
|
|
95
|
+
writes: {
|
|
96
|
+
enabled: writesEnabled,
|
|
97
|
+
reason: accepted
|
|
98
|
+
? "DRIFTED fingerprint accepted by user config (accepted-fingerprint) — writes " +
|
|
99
|
+
"enabled AT YOUR OWN RISK; every audit record carries fingerprint:user-accepted"
|
|
100
|
+
: writesEnabled
|
|
101
|
+
? "schema fingerprint matches shipped baseline"
|
|
102
|
+
: status.kind === "drift"
|
|
103
|
+
? "schema fingerprint deviates from baseline — writes disabled until revalidated"
|
|
104
|
+
: "unknown databaseVersion — update things-api or revalidate",
|
|
105
|
+
},
|
|
106
|
+
experimental: {
|
|
107
|
+
enabled: config.allowExperimental,
|
|
108
|
+
sdefDeclaresReorder: sdefCanary,
|
|
109
|
+
reason: !config.allowExperimental
|
|
110
|
+
? "off — native reorder disabled (`things config set allow-experimental true` to opt in)"
|
|
111
|
+
: sdefCanary
|
|
112
|
+
? "on — private reorder command still declared in the app sdef"
|
|
113
|
+
: "on BUT the app sdef no longer declares the private reorder command (removed by " +
|
|
114
|
+
"an update?) — native reorder is blocked by the canary",
|
|
115
|
+
},
|
|
116
|
+
environment: {
|
|
117
|
+
current: currentEnv,
|
|
118
|
+
lastVerifiedWrite: recordedEnv,
|
|
119
|
+
changes: diffEnvironment(recordedEnv, currentEnv),
|
|
120
|
+
},
|
|
121
|
+
automation: options.probeAutomation === true
|
|
122
|
+
? probeAutomation(options.probeDeps)
|
|
123
|
+
: {
|
|
124
|
+
status: "not-probed",
|
|
125
|
+
detail: "opt-in: pass --probe-automation to actively test Automation consent (may " +
|
|
126
|
+
"show a one-time macOS prompt; skipped when Things is not running)",
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
return {
|
|
130
|
+
report,
|
|
131
|
+
error: null,
|
|
132
|
+
exitCode: writesEnabled ? ExitCode.Ok : ExitCode.DriftBlocked,
|
|
133
|
+
meta: {
|
|
134
|
+
dbVersion: observation.databaseVersion,
|
|
135
|
+
fingerprint: fingerprintStatus === "unknown-version" ? "unknown" : fingerprintStatus,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
conn.close();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=diagnose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnose.js","sourceRoot":"","sources":["../src/diagnose.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* things-api — typed library for programmatic interaction with Things 3.
|
|
3
|
+
*
|
|
4
|
+
* Reads via direct SQLite; writes via the verified mutation pipeline over
|
|
5
|
+
* official app surfaces (URL scheme + AppleScript, both lab-validated).
|
|
6
|
+
* See docs/design/architecture.md.
|
|
7
|
+
*/
|
|
8
|
+
export { openThings } from "./client.ts";
|
|
9
|
+
export type { OpenOptions, ThingsClient } from "./client.ts";
|
|
10
|
+
export { diagnose } from "./diagnose.ts";
|
|
11
|
+
export type { DiagnoseOptions, DiagnoseReport, DiagnoseResult } from "./diagnose.ts";
|
|
12
|
+
export { probeAutomation } from "./write/automation-probe.ts";
|
|
13
|
+
export type { AutomationProbeDeps, AutomationProbeResult, AutomationProbeStatus, } from "./write/automation-probe.ts";
|
|
14
|
+
export { createEnvironmentTracker, describeEnvironmentChanges, diffEnvironment, } from "./write/environment.ts";
|
|
15
|
+
export type { EnvironmentChange, EnvironmentTracker, EnvironmentTuple, } from "./write/environment.ts";
|
|
16
|
+
export type { FailureHint, LikelyCause } from "./write/failure-hints.ts";
|
|
17
|
+
export { capabilitiesTable } from "./write/capabilities.ts";
|
|
18
|
+
export type { CapabilityEntry } from "./write/capabilities.ts";
|
|
19
|
+
export { saveConfigKey } from "./config.ts";
|
|
20
|
+
export { createThingsMcpServer } from "./mcp/server.ts";
|
|
21
|
+
export type { McpServerOptions } from "./mcp/server.ts";
|
|
22
|
+
export type { UndoItemResult, UndoOptions, UndoPlan, UndoStep } from "./write/undo.ts";
|
|
23
|
+
export type { BatchItemResult, BatchOp, BatchOptions } from "./write/batch.ts";
|
|
24
|
+
export type { ReorderResult } from "./write/reorder.ts";
|
|
25
|
+
export type { SearchOptions, SomedayFilter, UpcomingFilter, ViewFilter, ChangedItem, } from "./read/views.ts";
|
|
26
|
+
export type { Acknowledgements, AreaAddParams, ContainerRef, OperationKind, OperationParamsMap, ProjectAddParams, ProjectCompleteParams, ProjectMoveParams, ProjectUpdateParams, ReorderParams, ReorderScope, ReorderStrategy, TagAddParams, TagUpdateParams, AreaUpdateParams, TodoAddParams, TodoMoveParams, TodoUpdateParams, WhenValue, } from "./write/operations.ts";
|
|
27
|
+
export { OPERATION_KINDS } from "./write/operations.ts";
|
|
28
|
+
export type { MutationPlan, MutationResult, WriteOptions } from "./write/pipeline.ts";
|
|
29
|
+
export type { HazardId } from "./write/guards.ts";
|
|
30
|
+
export { HAZARD_IDS } from "./write/guards.ts";
|
|
31
|
+
export type { CompiledInvocation, VectorId, VectorMatrix, VectorSupport, WriteVector, } from "./write/vectors/types.ts";
|
|
32
|
+
export { APPLESCRIPT_MATRIX } from "./write/vectors/applescript.ts";
|
|
33
|
+
export { URL_SCHEME_MATRIX } from "./write/vectors/url-scheme.ts";
|
|
34
|
+
export type { DeltaSpec, FieldAssertion } from "./write/verify/delta.ts";
|
|
35
|
+
export type { AuditRecord } from "./audit/schema.ts";
|
|
36
|
+
export type { DisruptionTier, Profile, ThingsApiConfig } from "./config.ts";
|
|
37
|
+
export type { AnyTask, Area, ChecklistItem, Heading, IsoDateGroup, Project, Ref, RepeatingInfo, StartState, Tag, TaskStatus, TaskType, Todo, TodaySection, } from "./model/entities.ts";
|
|
38
|
+
export type { IsoDate } from "./model/dates.ts";
|
|
39
|
+
export type { ListItem, SidebarSection, TodayView } from "./read/views.ts";
|
|
40
|
+
export type { ProjectView } from "./read/project-view.ts";
|
|
41
|
+
export { ProjectNotFoundError } from "./read/project-view.ts";
|
|
42
|
+
export { ThingsDbNotFoundError } from "./db/locate.ts";
|
|
43
|
+
export { ThingsDbOpenError } from "./db/connection.ts";
|
|
44
|
+
export type { Baseline, FingerprintStatus, SchemaObservation } from "./db/fingerprint.ts";
|
|
45
|
+
export { API_VERSION, ExitCode, PKG_VERSION } from "./contracts.ts";
|
|
46
|
+
export type { Envelope, EnvelopeMeta, ErrorEnvelope, OkEnvelope } from "./contracts.ts";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* things-api — typed library for programmatic interaction with Things 3.
|
|
3
|
+
*
|
|
4
|
+
* Reads via direct SQLite; writes via the verified mutation pipeline over
|
|
5
|
+
* official app surfaces (URL scheme + AppleScript, both lab-validated).
|
|
6
|
+
* See docs/design/architecture.md.
|
|
7
|
+
*/
|
|
8
|
+
export { openThings } from "./client.js";
|
|
9
|
+
export { diagnose } from "./diagnose.js";
|
|
10
|
+
export { probeAutomation } from "./write/automation-probe.js";
|
|
11
|
+
export { createEnvironmentTracker, describeEnvironmentChanges, diffEnvironment, } from "./write/environment.js";
|
|
12
|
+
export { capabilitiesTable } from "./write/capabilities.js";
|
|
13
|
+
export { saveConfigKey } from "./config.js";
|
|
14
|
+
export { createThingsMcpServer } from "./mcp/server.js";
|
|
15
|
+
export { OPERATION_KINDS } from "./write/operations.js";
|
|
16
|
+
export { HAZARD_IDS } from "./write/guards.js";
|
|
17
|
+
export { APPLESCRIPT_MATRIX } from "./write/vectors/applescript.js";
|
|
18
|
+
export { URL_SCHEME_MATRIX } from "./write/vectors/url-scheme.js";
|
|
19
|
+
export { ProjectNotFoundError } from "./read/project-view.js";
|
|
20
|
+
export { ThingsDbNotFoundError } from "./db/locate.js";
|
|
21
|
+
export { ThingsDbOpenError } from "./db/connection.js";
|
|
22
|
+
export { API_VERSION, ExitCode, PKG_VERSION } from "./contracts.js";
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAM9D,OAAO,EACL,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAOhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAkCxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAQ/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAwBlE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGvD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { type OpenOptions } from "../client.ts";
|
|
3
|
+
export interface McpServerOptions {
|
|
4
|
+
dbPath?: string;
|
|
5
|
+
/** Test seam: forwarded to openThings (fake vectors, pinned clock, env). */
|
|
6
|
+
openOptions?: OpenOptions;
|
|
7
|
+
}
|
|
8
|
+
export declare function createThingsMcpServer(options?: McpServerOptions): McpServer;
|