@ahpd/server 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.
Files changed (170) hide show
  1. package/README.md +88 -81
  2. package/dist/config.d.ts +72 -0
  3. package/dist/config.js +75 -0
  4. package/dist/daemon.d.ts +31 -0
  5. package/dist/daemon.js +162 -0
  6. package/dist/main.d.ts +2 -0
  7. package/dist/main.js +335 -0
  8. package/dist/pty.d.ts +12 -0
  9. package/dist/pty.js +56 -0
  10. package/package.json +15 -25
  11. package/dist/automations.d.ts +0 -4
  12. package/dist/automations.d.ts.map +0 -1
  13. package/dist/automations.js +0 -191
  14. package/dist/automations.js.map +0 -1
  15. package/dist/catalog.d.ts +0 -48
  16. package/dist/catalog.d.ts.map +0 -1
  17. package/dist/catalog.js +0 -51
  18. package/dist/catalog.js.map +0 -1
  19. package/dist/changes.d.ts +0 -4
  20. package/dist/changes.d.ts.map +0 -1
  21. package/dist/changes.js +0 -601
  22. package/dist/changes.js.map +0 -1
  23. package/dist/cron.d.ts +0 -43
  24. package/dist/cron.d.ts.map +0 -1
  25. package/dist/cron.js +0 -211
  26. package/dist/cron.js.map +0 -1
  27. package/dist/git.d.ts +0 -16
  28. package/dist/git.d.ts.map +0 -1
  29. package/dist/git.js +0 -126
  30. package/dist/git.js.map +0 -1
  31. package/dist/host.d.ts +0 -24
  32. package/dist/host.d.ts.map +0 -1
  33. package/dist/host.js +0 -5850
  34. package/dist/host.js.map +0 -1
  35. package/dist/index.d.ts +0 -36
  36. package/dist/index.d.ts.map +0 -1
  37. package/dist/index.js +0 -34
  38. package/dist/index.js.map +0 -1
  39. package/dist/listen.d.ts +0 -3
  40. package/dist/listen.d.ts.map +0 -1
  41. package/dist/listen.js +0 -183
  42. package/dist/listen.js.map +0 -1
  43. package/dist/paging.d.ts +0 -38
  44. package/dist/paging.d.ts.map +0 -1
  45. package/dist/paging.js +0 -53
  46. package/dist/paging.js.map +0 -1
  47. package/dist/paths.d.ts +0 -15
  48. package/dist/paths.d.ts.map +0 -1
  49. package/dist/paths.js +0 -19
  50. package/dist/paths.js.map +0 -1
  51. package/dist/resources.d.ts +0 -122
  52. package/dist/resources.d.ts.map +0 -1
  53. package/dist/resources.js +0 -494
  54. package/dist/resources.js.map +0 -1
  55. package/dist/rpc.d.ts +0 -63
  56. package/dist/rpc.d.ts.map +0 -1
  57. package/dist/rpc.js +0 -208
  58. package/dist/rpc.js.map +0 -1
  59. package/dist/scheduled.d.ts +0 -48
  60. package/dist/scheduled.d.ts.map +0 -1
  61. package/dist/scheduled.js +0 -325
  62. package/dist/scheduled.js.map +0 -1
  63. package/dist/terminals.d.ts +0 -29
  64. package/dist/terminals.d.ts.map +0 -1
  65. package/dist/terminals.js +0 -346
  66. package/dist/terminals.js.map +0 -1
  67. package/dist/tools.d.ts +0 -18
  68. package/dist/tools.d.ts.map +0 -1
  69. package/dist/tools.js +0 -65
  70. package/dist/tools.js.map +0 -1
  71. package/dist/types/agent.d.ts +0 -245
  72. package/dist/types/agent.d.ts.map +0 -1
  73. package/dist/types/agent.js +0 -3
  74. package/dist/types/agent.js.map +0 -1
  75. package/dist/types/automations.d.ts +0 -164
  76. package/dist/types/automations.d.ts.map +0 -1
  77. package/dist/types/automations.js +0 -3
  78. package/dist/types/automations.js.map +0 -1
  79. package/dist/types/catalog.d.ts +0 -30
  80. package/dist/types/catalog.d.ts.map +0 -1
  81. package/dist/types/catalog.js +0 -3
  82. package/dist/types/catalog.js.map +0 -1
  83. package/dist/types/changes.d.ts +0 -272
  84. package/dist/types/changes.d.ts.map +0 -1
  85. package/dist/types/changes.js +0 -3
  86. package/dist/types/changes.js.map +0 -1
  87. package/dist/types/common.d.ts +0 -9
  88. package/dist/types/common.d.ts.map +0 -1
  89. package/dist/types/common.js +0 -3
  90. package/dist/types/common.js.map +0 -1
  91. package/dist/types/host.d.ts +0 -372
  92. package/dist/types/host.d.ts.map +0 -1
  93. package/dist/types/host.js +0 -3
  94. package/dist/types/host.js.map +0 -1
  95. package/dist/types/index.d.ts +0 -20
  96. package/dist/types/index.d.ts.map +0 -1
  97. package/dist/types/index.js +0 -8
  98. package/dist/types/index.js.map +0 -1
  99. package/dist/types/listen.d.ts +0 -48
  100. package/dist/types/listen.d.ts.map +0 -1
  101. package/dist/types/listen.js +0 -3
  102. package/dist/types/listen.js.map +0 -1
  103. package/dist/types/paging.d.ts +0 -10
  104. package/dist/types/paging.d.ts.map +0 -1
  105. package/dist/types/paging.js +0 -3
  106. package/dist/types/paging.js.map +0 -1
  107. package/dist/types/probe.d.ts +0 -46
  108. package/dist/types/probe.d.ts.map +0 -1
  109. package/dist/types/probe.js +0 -3
  110. package/dist/types/probe.js.map +0 -1
  111. package/dist/types/resources.d.ts +0 -83
  112. package/dist/types/resources.d.ts.map +0 -1
  113. package/dist/types/resources.js +0 -3
  114. package/dist/types/resources.js.map +0 -1
  115. package/dist/types/rpc.d.ts +0 -65
  116. package/dist/types/rpc.d.ts.map +0 -1
  117. package/dist/types/rpc.js +0 -3
  118. package/dist/types/rpc.js.map +0 -1
  119. package/dist/types/session.d.ts +0 -395
  120. package/dist/types/session.d.ts.map +0 -1
  121. package/dist/types/session.js +0 -3
  122. package/dist/types/session.js.map +0 -1
  123. package/dist/types/terminals.d.ts +0 -131
  124. package/dist/types/terminals.d.ts.map +0 -1
  125. package/dist/types/terminals.js +0 -3
  126. package/dist/types/terminals.js.map +0 -1
  127. package/dist/types/wire.d.ts +0 -62
  128. package/dist/types/wire.d.ts.map +0 -1
  129. package/dist/types/wire.js +0 -13
  130. package/dist/types/wire.js.map +0 -1
  131. package/dist/types/worktrees.d.ts +0 -86
  132. package/dist/types/worktrees.d.ts.map +0 -1
  133. package/dist/types/worktrees.js +0 -3
  134. package/dist/types/worktrees.js.map +0 -1
  135. package/dist/worktrees.d.ts +0 -27
  136. package/dist/worktrees.d.ts.map +0 -1
  137. package/dist/worktrees.js +0 -152
  138. package/dist/worktrees.js.map +0 -1
  139. package/src/automations.ts +0 -204
  140. package/src/catalog.ts +0 -53
  141. package/src/changes.ts +0 -629
  142. package/src/cron.ts +0 -251
  143. package/src/git.ts +0 -132
  144. package/src/host.ts +0 -5947
  145. package/src/index.ts +0 -40
  146. package/src/listen.ts +0 -240
  147. package/src/paging.ts +0 -56
  148. package/src/paths.ts +0 -19
  149. package/src/resources.ts +0 -513
  150. package/src/rpc.ts +0 -222
  151. package/src/scheduled.ts +0 -369
  152. package/src/terminals.ts +0 -353
  153. package/src/tools.ts +0 -64
  154. package/src/types/agent.ts +0 -255
  155. package/src/types/automations.ts +0 -164
  156. package/src/types/catalog.ts +0 -26
  157. package/src/types/changes.ts +0 -266
  158. package/src/types/common.ts +0 -9
  159. package/src/types/host.ts +0 -386
  160. package/src/types/index.ts +0 -27
  161. package/src/types/listen.ts +0 -53
  162. package/src/types/paging.ts +0 -11
  163. package/src/types/probe.ts +0 -42
  164. package/src/types/resources.ts +0 -107
  165. package/src/types/rpc.ts +0 -68
  166. package/src/types/session.ts +0 -415
  167. package/src/types/terminals.ts +0 -133
  168. package/src/types/wire.ts +0 -69
  169. package/src/types/worktrees.ts +0 -91
  170. package/src/worktrees.ts +0 -162
@@ -1,86 +0,0 @@
1
- /** A working tree of its own, so two sessions in one repository do not collide. */
2
- /** What to make, and from where. */
3
- export interface Worktree {
4
- /** The repository to make it from. A path, not a URI. */
5
- repository: string;
6
- /** The branch to start from. */
7
- base: string;
8
- /**
9
- * The branch to create and check out, or nothing to check out `base` itself.
10
- *
11
- * Absent is what `worktreeCreateNewBranch: false` means: the session
12
- * continues an existing branch rather than starting one. Git refuses a
13
- * second worktree on a branch already checked out somewhere, which is the
14
- * right refusal - two sessions on one branch is the collision worktrees
15
- * exist to prevent.
16
- */
17
- branch?: string;
18
- /**
19
- * Whether the created branch tracks the upstream of the one it started from.
20
- *
21
- * Off unless asked. A tracking branch pushes at its upstream by default, so
22
- * a `git push` inside a session's worktree would go at the branch it was
23
- * started *from* - which is the one mistake in here that reaches a shared
24
- * repository.
25
- */
26
- track?: boolean;
27
- /** Where to put it. */
28
- path: string;
29
- /**
30
- * Git-ignored files to copy in, as glob patterns relative to the repository.
31
- *
32
- * A checkout carries what git tracks and nothing else, so a worktree of an
33
- * ordinary project has no `.env`, no `node_modules`, and none of the local
34
- * configuration the thing needs to run. Without these the isolation works
35
- * and the session inside it cannot build - which is a failure the person
36
- * meets after choosing it rather than while choosing it.
37
- */
38
- include?: string[];
39
- }
40
- /**
41
- * Making and unmaking git worktrees.
42
- *
43
- * A port for the reason `DirectoryFacts` and `ChangesetSource` are ports: it
44
- * spawns `git`, which is a binary rather than a runtime API and may not be
45
- * installed. A host given none advertises no `isolation` at all, which is the
46
- * honest answer - a client draws the control a host says it has.
47
- *
48
- * Deliberately not part of `ChangesetSource`. That port describes changes; a
49
- * worktree is a directory, and the two happen to share an implementation
50
- * rather than a subject.
51
- */
52
- export interface Worktrees {
53
- /**
54
- * The repository root above this directory, if it is in one.
55
- *
56
- * The root and not the directory: a worktree is made from the repository,
57
- * and a session pointed at `src/` inside one is still a session in it.
58
- * Undefined means there is no repository here and no isolation to offer.
59
- */
60
- repository(dir: string): Promise<string | undefined>;
61
- /**
62
- * The branches this repository has, most useful first.
63
- *
64
- * What a client draws the base-branch picker from. An empty list is a real
65
- * answer - a repository with no commits has no branches - and leaves the
66
- * control with nothing to choose but the default.
67
- */
68
- branches(repository: string): Promise<string[]>;
69
- /** Make one. Answers the path it made, which is where the session runs. */
70
- create(worktree: Worktree): Promise<void>;
71
- /**
72
- * Whether anything in it is uncommitted.
73
- *
74
- * Asked before it is removed, and the answer decides whether it is: work
75
- * nobody committed is work this daemon cannot judge the value of.
76
- */
77
- dirty(path: string): Promise<boolean>;
78
- /**
79
- * Take one away, and the branch with it if nothing else points at it.
80
- *
81
- * Only ever called for a worktree this host made and only when `dirty`
82
- * answered no.
83
- */
84
- remove(repository: string, path: string, branch?: string): Promise<void>;
85
- }
86
- //# sourceMappingURL=worktrees.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktrees.d.ts","sourceRoot":"","sources":["../../src/types/worktrees.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAEnF,oCAAoC;AACpC,MAAM,WAAW,QAAQ;IACvB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAErD;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEhD,2EAA2E;IAC3E,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1C;;;;;OAKG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEtC;;;;;OAKG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1E"}
@@ -1,3 +0,0 @@
1
- /** A working tree of its own, so two sessions in one repository do not collide. */
2
- export {};
3
- //# sourceMappingURL=worktrees.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktrees.js","sourceRoot":"","sources":["../../src/types/worktrees.ts"],"names":[],"mappings":"AAAA,mFAAmF"}
@@ -1,27 +0,0 @@
1
- /** Git worktrees, so two sessions in one repository do not edit under each other. */
2
- import type { Worktrees } from './types/worktrees.js';
3
- /**
4
- * Where a repository's worktrees live: `<repo>.worktrees`, beside it.
5
- *
6
- * Beside rather than inside, because a worktree inside its own repository is
7
- * a directory git then has to be told to ignore, and every tool that walks the
8
- * tree finds the same project twice. The name is the one VS Code's host uses,
9
- * so a worktree either of them makes is one the other finds.
10
- */
11
- export declare const worktreesOf: (repository: string) => string;
12
- /** The directory name for a branch: no slashes, since it has to be one segment. */
13
- export declare const worktreeFor: (branch: string) => string;
14
- /**
15
- * Git worktrees, as a host's `Worktrees` port.
16
- *
17
- * Deliberately not part of `createHost`: it spawns `git`, which is a binary
18
- * rather than a runtime API and may not be installed at all. A host without it
19
- * offers no isolation and every session runs in the folder it was pointed at,
20
- * which is what this daemon did before there was a choice.
21
- *
22
- * ```ts
23
- * createHost({ path, agents: [claude({ paths })], worktrees: gitWorktrees() });
24
- * ```
25
- */
26
- export declare function gitWorktrees(): Worktrees;
27
- //# sourceMappingURL=worktrees.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktrees.d.ts","sourceRoot":"","sources":["../src/worktrees.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAKrF,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAchE;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,GAAI,YAAY,MAAM,KAAG,MACe,CAAC;AAEjE,mFAAmF;AACnF,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,KAAG,MACQ,CAAC;AAEtD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,SAAS,CAmFxC"}
package/dist/worktrees.js DELETED
@@ -1,152 +0,0 @@
1
- /** Git worktrees, so two sessions in one repository do not edit under each other. */
2
- import { execFile } from 'node:child_process';
3
- import { basename, dirname, join } from 'node:path';
4
- import { cp, mkdir, readdir, stat } from 'node:fs/promises';
5
- /** Run git, and answer what it said. Rejects with git's own words. */
6
- const git = (dir, args, ms = 60_000) => new Promise((resolve, reject) => {
7
- execFile('git', ['-C', dir, ...args], { timeout: ms, maxBuffer: 8 << 20 }, (error, out, bad) => {
8
- if (!error)
9
- return resolve(out.toString());
10
- // git's own message, not node's: "fatal: a branch named x already
11
- // exists" is something a person can act on and `Command failed` is not.
12
- const said = bad.toString().trim() || error.message;
13
- reject(new Error(said));
14
- });
15
- });
16
- /**
17
- * Where a repository's worktrees live: `<repo>.worktrees`, beside it.
18
- *
19
- * Beside rather than inside, because a worktree inside its own repository is
20
- * a directory git then has to be told to ignore, and every tool that walks the
21
- * tree finds the same project twice. The name is the one VS Code's host uses,
22
- * so a worktree either of them makes is one the other finds.
23
- */
24
- export const worktreesOf = (repository) => join(dirname(repository), `${basename(repository)}.worktrees`);
25
- /** The directory name for a branch: no slashes, since it has to be one segment. */
26
- export const worktreeFor = (branch) => branch.replace(/^agents\//, '').replace(/\//g, '-');
27
- /**
28
- * Git worktrees, as a host's `Worktrees` port.
29
- *
30
- * Deliberately not part of `createHost`: it spawns `git`, which is a binary
31
- * rather than a runtime API and may not be installed at all. A host without it
32
- * offers no isolation and every session runs in the folder it was pointed at,
33
- * which is what this daemon did before there was a choice.
34
- *
35
- * ```ts
36
- * createHost({ path, agents: [claude({ paths })], worktrees: gitWorktrees() });
37
- * ```
38
- */
39
- export function gitWorktrees() {
40
- return {
41
- repository: async (dir) => {
42
- const found = await git(dir, ['rev-parse', '--show-toplevel'], 5_000).catch(() => undefined);
43
- const root = found?.trim();
44
- return root === undefined || root === '' ? undefined : root;
45
- },
46
- branches: async (repository) => {
47
- /*
48
- * Local branches by most recent commit, then the remote ones.
49
- *
50
- * Ordered rather than alphabetical because the useful answer is almost
51
- * always near the top of the first list, and a picker that opens on
52
- * `archive/2019-cleanup` is one somebody has to search.
53
- */
54
- const said = await git(repository, ['for-each-ref', '--sort=-committerdate', '--format=%(refname:short)', 'refs/heads', 'refs/remotes'], 10_000).catch(() => '');
55
- const seen = new Set();
56
- for (const line of said.split('\n')) {
57
- const name = line.trim();
58
- // `origin/HEAD` is a symbolic ref rather than a branch, and checking
59
- // it out is a detached head instead of the branch somebody meant.
60
- if (name === '' || name.endsWith('/HEAD'))
61
- continue;
62
- seen.add(name.replace(/^origin\//, ''));
63
- }
64
- return [...seen];
65
- },
66
- create: async (worktree) => {
67
- await mkdir(dirname(worktree.path), { recursive: true });
68
- /*
69
- * A new branch, or the one that was chosen.
70
- *
71
- * `--no-track` unless asked, because the new branch is this session's
72
- * own: tracking would make it push to the base branch's upstream by
73
- * default, so a `git push` inside a worktree session would go at the
74
- * branch it was started *from*. That is the one mistake in here that
75
- * reaches a shared repository.
76
- *
77
- * No branch at all is `worktreeCreateNewBranch: false` - the session
78
- * continues `base` rather than starting something. Git refuses a second
79
- * worktree on a branch already checked out, and that refusal is right.
80
- */
81
- await git(worktree.repository, worktree.branch === undefined
82
- ? ['worktree', 'add', worktree.path, worktree.base]
83
- : [
84
- 'worktree', 'add', worktree.track === true ? '--track' : '--no-track',
85
- '-b', worktree.branch, worktree.path, worktree.base,
86
- ]);
87
- for (const pattern of worktree.include ?? []) {
88
- // Best effort, one pattern at a time: a `.env` that is not there is
89
- // the ordinary case, and a session that refused to start over a
90
- // missing optional file would be worse than one without it.
91
- await copy(worktree.repository, worktree.path, pattern).catch(() => undefined);
92
- }
93
- },
94
- dirty: async (path) => {
95
- // `--porcelain` says nothing at all about a clean tree, which makes the
96
- // empty answer the reliable one. Untracked files count: a file the
97
- // agent wrote and never added is exactly the work worth keeping.
98
- const said = await git(path, ['status', '--porcelain'], 10_000).catch(() => 'unknown');
99
- return said.trim() !== '';
100
- },
101
- remove: async (repository, path, branch) => {
102
- await git(repository, ['worktree', 'remove', path]);
103
- /*
104
- * The branch too, and only the one this host made.
105
- *
106
- * Named by the caller rather than derived from the directory: a branch
107
- * prefix a client asked for changes the name, and guessing it wrong
108
- * either deletes nothing or - far worse - names somebody else's. `-d`
109
- * refuses to delete a branch carrying commits nothing else has, which
110
- * is the same judgement the dirty check makes about uncommitted work.
111
- */
112
- if (branch !== undefined)
113
- await git(repository, ['branch', '-d', branch]).catch(() => undefined);
114
- },
115
- };
116
- }
117
- /**
118
- * Copy one git-ignored path into the new worktree, keeping its place.
119
- *
120
- * A glob only in the shallow sense the reference uses it: a literal path, or
121
- * one trailing `*` in the last segment. A full matcher here would be a
122
- * dependency and a surprise - the patterns clients send are `.env`,
123
- * `.env.local` and `node_modules`.
124
- */
125
- const copy = async (from, to, pattern) => {
126
- if (pattern.includes('..') || pattern.startsWith('/'))
127
- return;
128
- const star = pattern.lastIndexOf('*');
129
- if (star === -1) {
130
- await cp(join(from, pattern), join(to, pattern), { recursive: true, errorOnExist: false });
131
- return;
132
- }
133
- const at = pattern.lastIndexOf('/');
134
- const dir = at === -1 ? '' : pattern.slice(0, at);
135
- const leaf = at === -1 ? pattern : pattern.slice(at + 1);
136
- if (leaf.indexOf('*') !== leaf.lastIndexOf('*'))
137
- return;
138
- const [before, after] = leaf.split('*');
139
- const entries = await readdir(join(from, dir)).catch(() => []);
140
- for (const entry of entries) {
141
- if (!entry.startsWith(before ?? '') || !entry.endsWith(after ?? ''))
142
- continue;
143
- const source = join(from, dir, entry);
144
- // Directories as well as files: `node_modules` is the pattern that makes
145
- // the difference between a worktree that builds and one that does not.
146
- const what = await stat(source).catch(() => undefined);
147
- if (!what)
148
- continue;
149
- await cp(source, join(to, dir, entry), { recursive: what.isDirectory(), errorOnExist: false });
150
- }
151
- };
152
- //# sourceMappingURL=worktrees.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktrees.js","sourceRoot":"","sources":["../src/worktrees.ts"],"names":[],"mappings":"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"}
@@ -1,204 +0,0 @@
1
- /** Automations held in memory, run when somebody asks. */
2
-
3
- import { randomUUID } from 'node:crypto';
4
- import type { Automation, AutomationRun, AutomationStore, StartSession } from './types/automations.js';
5
- import type { Bag } from './types/common.js';
6
-
7
- /**
8
- * The automations of a host that does not schedule.
9
- *
10
- * Deliberately half a store, and the half worth having first: definitions can
11
- * be written, patched, listed and *run*, and nothing here holds a clock. An
12
- * automation with a schedule trigger is accepted, kept and reported with no
13
- * `nextRunAt` - which is the honest form of "this host will not fire that",
14
- * and is what stops a client drawing a next-run time that will never arrive.
15
- *
16
- * Everything is in memory, so it goes when the process does. A store that
17
- * outlives a restart is a different implementation of the same interface, and
18
- * the reason the interface exists.
19
- */
20
-
21
- const now = (): string => new Date().toISOString();
22
-
23
- /** How many runs a summary list carries before it needs a cursor. */
24
- const PAGE = 20;
25
-
26
- export function memoryAutomations(): AutomationStore {
27
- const held = new Map<string, Automation>();
28
- /** Runs by automation, newest first. */
29
- const history = new Map<string, AutomationRun[]>();
30
- /** Every run by its own URI, for the channel a client watches it on. */
31
- const byRun = new Map<string, AutomationRun>();
32
- const listeners: ((event: { automation?: string; run?: string; removed?: string }) => void)[] = [];
33
- const said = (event: { automation?: string; run?: string; removed?: string }): void => {
34
- for (const listener of listeners) listener(event);
35
- };
36
-
37
- /** The summary form of a run, which is what an automation carries. */
38
- const summary = (run: AutomationRun): Bag => ({
39
- resource: run.resource,
40
- automation: run.automation,
41
- origin: run.origin,
42
- lifecycle: run.lifecycle,
43
- sessionCount: run.sessions.length,
44
- ...(run.primarySession !== undefined ? { primarySession: run.primarySession } : {}),
45
- });
46
-
47
- /** Rebuild the entry a client reads, so `runs` and `operations` are never stale. */
48
- const entry = (automation: Automation): Automation => {
49
- const enabled = automation.definition.enabled !== false;
50
- return {
51
- ...automation,
52
- runs: (history.get(automation.resource) ?? []).slice(0, PAGE).map(summary),
53
- ...((history.get(automation.resource) ?? []).length > PAGE
54
- ? { runsNextCursor: String(PAGE) }
55
- : {}),
56
- // `run` only where it would do something. A disabled automation is one
57
- // somebody switched off, and offering the button anyway is a control
58
- // that argues with the switch beside it.
59
- operations: enabled ? ['update', 'remove', 'run'] : ['update', 'remove'],
60
- };
61
- };
62
-
63
- return {
64
- list: () => [...held.values()].map(entry),
65
- get: (resource) => {
66
- const found = held.get(resource);
67
- return found && entry(found);
68
- },
69
-
70
- /*
71
- * What this store understands, which is nothing.
72
- *
73
- * This command answers with *event* triggers. Schedule triggers are
74
- * protocol-defined and never listed here, and manual is not a trigger at
75
- * all - the protocol says an empty trigger list is what manual-only means.
76
- * So a store with no event triggers answers with none, and a client reads
77
- * "this host will not fire that" from the absent `nextRunAt` instead.
78
- */
79
- triggers: () => [],
80
-
81
- create: (resource, definition) => {
82
- const at = now();
83
- const made: Automation = {
84
- resource,
85
- definition,
86
- runs: [],
87
- operations: [],
88
- createdAt: at,
89
- modifiedAt: at,
90
- };
91
- held.set(resource, made);
92
- said({ automation: resource });
93
- return entry(made);
94
- },
95
-
96
- update: (resource, changes) => {
97
- const found = held.get(resource);
98
- if (!found) return undefined;
99
- // A patch: absent keys are left alone, which is the whole difference
100
- // between this and a write. A client sending the whole definition back
101
- // would otherwise revert whatever another client changed meanwhile.
102
- const after: Automation = {
103
- ...found,
104
- definition: { ...found.definition, ...changes },
105
- modifiedAt: now(),
106
- };
107
- held.set(resource, after);
108
- said({ automation: resource });
109
- return entry(after);
110
- },
111
-
112
- remove: (resource) => {
113
- if (!held.delete(resource)) return false;
114
- for (const run of history.get(resource) ?? []) byRun.delete(run.resource);
115
- history.delete(resource);
116
- said({ removed: resource });
117
- return true;
118
- },
119
-
120
- run: async (resource, origin, start) => {
121
- const found = held.get(resource);
122
- if (!found) return undefined;
123
- if (found.definition.enabled === false) return undefined;
124
- const template = (typeof found.definition.session === 'object' && found.definition.session !== null
125
- ? found.definition.session
126
- : {}) as Bag;
127
- const message = (typeof found.definition.message === 'object' && found.definition.message !== null
128
- ? found.definition.message
129
- : {}) as Bag;
130
-
131
- const run: AutomationRun = {
132
- resource: `ahp-automation-run:/${randomUUID()}`,
133
- automation: resource,
134
- origin,
135
- lifecycle: { status: 'pending', createdAt: now() },
136
- sessions: [],
137
- };
138
- byRun.set(run.resource, run);
139
- const past = history.get(resource) ?? [];
140
- // Newest first, which is the order a client shows them in.
141
- history.set(resource, [run, ...past]);
142
- said({ automation: resource, run: run.resource });
143
-
144
- const directories = Array.isArray(template.workingDirectories) ? template.workingDirectories : [];
145
- const where = typeof directories[0] === 'string'
146
- ? (directories[0] as string).replace(/^file:\/\//, '')
147
- : undefined;
148
- const options: StartSession = {
149
- ...(typeof template.provider === 'string' ? { provider: template.provider } : {}),
150
- ...(where !== undefined ? { workingDirectory: where } : {}),
151
- ...(typeof template.config === 'object' && template.config !== null
152
- ? { config: template.config as Record<string, string> }
153
- : {}),
154
- text: typeof message.text === 'string' ? message.text : String(found.definition.title ?? ''),
155
- origin: { kind: 'automation', automation: resource, run: run.resource },
156
- };
157
-
158
- try {
159
- const session = await start(options);
160
- run.sessions = [session];
161
- run.primarySession = session;
162
- run.lifecycle = { ...run.lifecycle, status: 'running', startedAt: now() };
163
- }
164
- catch (error) {
165
- // Failed, and why. A run that vanished would be indistinguishable from
166
- // one that never started.
167
- run.lifecycle = {
168
- ...run.lifecycle,
169
- status: 'failed',
170
- endedAt: now(),
171
- error: { message: error instanceof Error ? error.message : String(error) },
172
- };
173
- }
174
- said({ automation: resource, run: run.resource });
175
- return run;
176
- },
177
-
178
- runOf: (resource) => byRun.get(resource),
179
-
180
- unlink: (resource, session) => {
181
- const run = byRun.get(resource);
182
- if (run === undefined || !run.sessions.includes(session)) return false;
183
- run.sessions = run.sessions.filter((one) => one !== session);
184
- // The protocol says removing the primary clears it. A run pointing at a
185
- // session that is gone is a run a client opens onto nothing.
186
- if (run.primarySession === session) delete run.primarySession;
187
- said({ automation: run.automation, run: run.resource });
188
- return true;
189
- },
190
-
191
- runs: (resource, cursor) => {
192
- const all = history.get(resource) ?? [];
193
- const from = cursor === undefined ? 0 : Number(cursor);
194
- const at = Number.isFinite(from) && from >= 0 ? from : 0;
195
- const page = all.slice(at, at + PAGE);
196
- return {
197
- items: page.map(summary),
198
- ...(at + PAGE < all.length ? { nextCursor: String(at + PAGE) } : {}),
199
- };
200
- },
201
-
202
- onChanged: (observer) => { listeners.push(observer); },
203
- };
204
- }
package/src/catalog.ts DELETED
@@ -1,53 +0,0 @@
1
- /**
2
- * How a session is named, and what its status bits are worth.
3
- *
4
- * URIs and numbers, and nothing that knows what a session *is*: the listing
5
- * itself belongs to whichever backend wrote the sessions down, because only
6
- * that one can say which exist. See `catalogue` in `@ahpd/agent-claude`.
7
- */
8
-
9
- /**
10
- * `SessionStatus`, as values.
11
- *
12
- * The protocol declares it as a `const enum`, which exists only in the type
13
- * system - importing it at runtime is a value import of something that was
14
- * erased. So the numbers are restated here, and they are the protocol's:
15
- * `InputNeeded` is 24 and *carries* `InProgress` (8), which is why anything
16
- * testing activity has to test it first.
17
- */
18
- export const Status = {
19
- Idle: 1,
20
- Error: 2,
21
- InProgress: 8,
22
- InputNeeded: 24,
23
- IsRead: 32,
24
- IsArchived: 64,
25
- } as const;
26
-
27
- /**
28
- * A URI for a session this host found on disk. `ahp-session:/<uuid>`, and the
29
- * SDK's id is already a uuid.
30
- *
31
- * Only for rows this host names itself. A session a *client* created is named
32
- * by that client, in whatever scheme it likes - see {@link idOf}.
33
- */
34
- export const uriFor = (sessionId: string): string => `ahp-session:/${sessionId}`;
35
-
36
- /**
37
- * The id inside a channel URI, whatever scheme the client chose.
38
- *
39
- * A channel URI is the **client's** to name and this host's to echo. VS Code
40
- * names a session after its provider - `claude:/<uuid>`, with the provider as
41
- * the *scheme* - and its terminals `agenthost-terminal:/<uuid>`. This used to
42
- * strip a literal `ahp-session:/` and refuse anything else, so every session
43
- * and every terminal VS Code opened was answered `is not a session URI`.
44
- *
45
- * So: everything after the scheme, without its leading slashes. The result is
46
- * an opaque key, not something to parse further.
47
- */
48
- export const idOf = (uri: string): string => {
49
- const colon = uri.indexOf(':');
50
- return (colon < 0 ? uri : uri.slice(colon + 1)).replace(/^\/+/, '');
51
- };
52
- /** The same, and the name it goes by where a session rather than a chat is meant. */
53
- export const idFor = idOf;