@chronicle.app/things-todo 0.1.0 → 0.3.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/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @chronicle.app/things-todo
|
|
2
2
|
|
|
3
|
-
Read Things 3 tasks/projects with `ThingsTodoExtractor`, then map task lifecycle events with `ThingsTodoTransformer`. The default path locates the installed Things database; pass `input` to use a consistent backup or fixture.
|
|
3
|
+
Read Things 3 tasks/projects with `ThingsTodoExtractor`, then map task lifecycle events with `ThingsTodoTransformer`. The default path locates the installed Things database; pass `input` to use a consistent backup or fixture. The task owner is a source-local `Agent` tagged `@me`, named after the OS account's full name (`id -F` on macOS); set `agentName` to use another name. The name is a label only: the owner is keyed by `['@type', 'source']` and gets no invented identifiers. Elsewhere, or when the account has no full name, the owner has no name.
|
|
4
4
|
|
|
5
5
|
Uses read-only `node:sqlite`. `since`/`until` are inclusive modification-date bounds; `limit: 0` is unlimited. Records are newest-modified first. Raw project/heading records enrich task relationships; the transformer emits task actions (plan, update, complete, cancel, delete), not standalone project actions. Source timestamps remain event timestamps; no store annotations are generated.
|
|
6
6
|
|
|
@@ -11,6 +11,8 @@ export declare class ThingsTodoExtractor extends SqliteExtractor<typeof ThingsTo
|
|
|
11
11
|
static default: boolean;
|
|
12
12
|
static schema: any;
|
|
13
13
|
static defaultTransformer: typeof ThingsTodoTransformer;
|
|
14
|
+
/** The owner's name from the local machine, used when `agentName` isn't set. */
|
|
15
|
+
protected resolveAgentName(): string | undefined;
|
|
14
16
|
private getStatusLabel;
|
|
15
17
|
extract(): AsyncGenerator<Record>;
|
|
16
18
|
private getEnrichedAreas;
|
|
@@ -2,6 +2,7 @@ import { SqliteExtractor, timeRangeConditions } from '@chronicle.app/etl-sqlite'
|
|
|
2
2
|
import { existsSync, readdirSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { localAccountName } from './ownerName.js';
|
|
5
6
|
import ThingsTodoTransformer from './ThingsTodoTransformer.js';
|
|
6
7
|
/**
|
|
7
8
|
* Resolve the Things 3 SQLite database path.
|
|
@@ -44,9 +45,16 @@ export class ThingsTodoExtractor extends SqliteExtractor {
|
|
|
44
45
|
.string()
|
|
45
46
|
.describe('The db file to read from')
|
|
46
47
|
.default(() => resolveThingsDbPath()),
|
|
47
|
-
agentName: z
|
|
48
|
+
agentName: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Name of the task owner. Defaults to the OS account's full name"),
|
|
48
52
|
});
|
|
49
53
|
static defaultTransformer = ThingsTodoTransformer;
|
|
54
|
+
/** The owner's name from the local machine, used when `agentName` isn't set. */
|
|
55
|
+
resolveAgentName() {
|
|
56
|
+
return localAccountName();
|
|
57
|
+
}
|
|
50
58
|
getStatusLabel(status) {
|
|
51
59
|
switch (status) {
|
|
52
60
|
case 0:
|
|
@@ -81,6 +89,7 @@ export class ThingsTodoExtractor extends SqliteExtractor {
|
|
|
81
89
|
values.push(limit);
|
|
82
90
|
}
|
|
83
91
|
const rows = this.db.prepare(sql).all(...values);
|
|
92
|
+
const agentName = this.config.agentName ?? this.resolveAgentName();
|
|
84
93
|
// Get enriched areas and projects with nested data
|
|
85
94
|
const areaIds = [...new Set(rows.map(row => row.area))].filter(Boolean);
|
|
86
95
|
const enrichedAreas = await this.getEnrichedAreas(areaIds);
|
|
@@ -127,8 +136,6 @@ export class ThingsTodoExtractor extends SqliteExtractor {
|
|
|
127
136
|
lastUsedAt: hierarchy?.lastUsedAt || null,
|
|
128
137
|
};
|
|
129
138
|
});
|
|
130
|
-
// Get user's real name for Person object deduplication
|
|
131
|
-
const realName = this.config.agentName;
|
|
132
139
|
// Determine the project relationship
|
|
133
140
|
let projectRelation = null;
|
|
134
141
|
let headingRelation = null;
|
|
@@ -158,9 +165,7 @@ export class ThingsTodoExtractor extends SqliteExtractor {
|
|
|
158
165
|
heading: headingRelation,
|
|
159
166
|
area: row.area ? enrichedAreas.get(row.area) || null : null,
|
|
160
167
|
tags: enrichedTags,
|
|
161
|
-
agent: {
|
|
162
|
-
name: realName, // Just the real name for Person [@type, title, source] deduplication
|
|
163
|
-
},
|
|
168
|
+
agent: { name: agentName },
|
|
164
169
|
});
|
|
165
170
|
}
|
|
166
171
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface OwnerNameOptions {
|
|
2
|
+
platform?: string;
|
|
3
|
+
run?: (command: string, args: string[]) => string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The full name of the OS account running the extraction, as the owner of the
|
|
7
|
+
* local Things database. On macOS this is the account's full name from
|
|
8
|
+
* `id -F`. Returns undefined elsewhere or when the name is unset or unreadable.
|
|
9
|
+
*/
|
|
10
|
+
export declare function localAccountName(options?: OwnerNameOptions): string | undefined;
|
|
11
|
+
//# sourceMappingURL=ownerName.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* The full name of the OS account running the extraction, as the owner of the
|
|
4
|
+
* local Things database. On macOS this is the account's full name from
|
|
5
|
+
* `id -F`. Returns undefined elsewhere or when the name is unset or unreadable.
|
|
6
|
+
*/
|
|
7
|
+
export function localAccountName(options = {}) {
|
|
8
|
+
if ((options.platform ?? process.platform) !== 'darwin')
|
|
9
|
+
return undefined;
|
|
10
|
+
const run = options.run ??
|
|
11
|
+
((command, args) => execFileSync(command, args, {
|
|
12
|
+
encoding: 'utf8',
|
|
13
|
+
timeout: 5000,
|
|
14
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
15
|
+
}));
|
|
16
|
+
try {
|
|
17
|
+
return run('/usr/bin/id', ['-F']).trim() || undefined;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chronicle.app/things-todo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"type": "module",
|
|
20
20
|
"description": "Chronicle things-todo extraction plugin",
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@chronicle.app/
|
|
22
|
+
"@chronicle.app/etl": "0.3.0",
|
|
23
|
+
"@chronicle.app/schema": "0.3.0",
|
|
24
|
+
"@chronicle.app/tsconfig": "0.3.0",
|
|
23
25
|
"@types/node": "^22.10.1",
|
|
24
26
|
"typescript": "^5.7.2"
|
|
25
27
|
},
|
|
@@ -34,10 +36,12 @@
|
|
|
34
36
|
"registry": "https://registry.npmjs.org/"
|
|
35
37
|
},
|
|
36
38
|
"dependencies": {
|
|
37
|
-
"@chronicle.app/etl": "0.
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
"@chronicle.app/etl-sqlite": "0.3.0",
|
|
40
|
+
"zod": "^3.24.1"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@chronicle.app/etl": ">=0.1.0 <1.0.0",
|
|
44
|
+
"@chronicle.app/schema": ">=0.1.0 <1.0.0"
|
|
41
45
|
},
|
|
42
46
|
"engines": {
|
|
43
47
|
"node": ">=22.13.0"
|
|
@@ -49,7 +53,23 @@
|
|
|
49
53
|
}
|
|
50
54
|
},
|
|
51
55
|
"chronicle": {
|
|
52
|
-
"plugin": true
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
"plugin": true,
|
|
57
|
+
"sources": {
|
|
58
|
+
"things-todo": {
|
|
59
|
+
"strategies": {
|
|
60
|
+
"app-db": {
|
|
61
|
+
"delivery": "local",
|
|
62
|
+
"recordTypes": [
|
|
63
|
+
"tasks",
|
|
64
|
+
"projects"
|
|
65
|
+
],
|
|
66
|
+
"default": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"platforms": [
|
|
70
|
+
"darwin"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
55
75
|
}
|