@prestack/thread 0.1.3 → 0.1.5

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 (270) hide show
  1. package/CHANGELOG.md +563 -0
  2. package/dist/core/actor.d.ts +1 -1
  3. package/dist/core/actor.js +5 -4
  4. package/dist/core/actor.ts +5 -4
  5. package/dist/core/attach.d.ts +0 -7
  6. package/dist/core/attach.js +38 -67
  7. package/dist/core/attach.ts +34 -80
  8. package/dist/core/chain.js +13 -17
  9. package/dist/core/chain.ts +11 -15
  10. package/dist/core/condition.d.ts +4 -4
  11. package/dist/core/condition.js +2 -2
  12. package/dist/core/descend.d.ts +4 -2
  13. package/dist/core/descend.js +91 -23
  14. package/dist/core/descend.ts +90 -21
  15. package/dist/core/errors/catalogue.d.ts +52 -0
  16. package/dist/{operations/errors/errors.js → core/errors/catalogue.js} +1 -1
  17. package/dist/{operations/errors/errors.ts → core/errors/catalogue.ts} +1 -1
  18. package/dist/{operations/errors/catalog.d.ts → core/errors/error.d.ts} +2 -3
  19. package/dist/{operations/errors/catalog.js → core/errors/error.js} +1 -16
  20. package/dist/{operations/errors/catalog.ts → core/errors/error.ts} +3 -19
  21. package/dist/core/errors/namespaces.d.ts +3 -0
  22. package/dist/core/errors/namespaces.js +15 -0
  23. package/dist/core/errors/namespaces.ts +19 -0
  24. package/dist/core/finding.js +2 -2
  25. package/dist/core/finding.ts +1 -2
  26. package/dist/core/init.d.ts +1 -1
  27. package/dist/core/init.js +51 -5
  28. package/dist/core/init.ts +58 -5
  29. package/dist/core/invoked.d.ts +1 -1
  30. package/dist/core/invoked.js +3 -1
  31. package/dist/core/invoked.ts +5 -2
  32. package/dist/core/round.js +36 -22
  33. package/dist/core/round.ts +26 -9
  34. package/dist/core/rows.d.ts +4 -0
  35. package/dist/core/rows.js +31 -2
  36. package/dist/core/rows.ts +33 -1
  37. package/dist/core/scope.d.ts +1 -0
  38. package/dist/core/scope.js +1 -0
  39. package/dist/core/scope.ts +2 -0
  40. package/dist/core/thread.d.ts +3 -3
  41. package/dist/core/tree.d.ts +4 -1
  42. package/dist/core/tree.js +26 -6
  43. package/dist/core/tree.ts +29 -6
  44. package/dist/core/types.d.ts +3 -3
  45. package/dist/core/types.js +3 -1
  46. package/dist/core/types.ts +2 -1
  47. package/dist/derivation/attach.js +3 -2
  48. package/dist/derivation/attach.ts +2 -1
  49. package/dist/derivation/generators/build.js +6 -6
  50. package/dist/derivation/generators/build.ts +1 -1
  51. package/dist/derivation/generators/check.js +1 -1
  52. package/dist/derivation/generators/determinism.js +1 -1
  53. package/dist/derivation/generators/release.js +60 -47
  54. package/dist/derivation/generators/release.ts +66 -43
  55. package/dist/derivation/release.d.ts +7 -3
  56. package/dist/derivation/release.js +14 -1
  57. package/dist/derivation/release.ts +22 -2
  58. package/dist/integrations/attach.d.ts +4 -0
  59. package/dist/integrations/attach.js +37 -56
  60. package/dist/integrations/attach.ts +54 -60
  61. package/dist/integrations/cli.d.ts +37 -0
  62. package/dist/integrations/cli.js +440 -0
  63. package/dist/integrations/cli.ts +543 -0
  64. package/dist/{core/files.d.ts → integrations/disk.d.ts} +16 -24
  65. package/dist/integrations/disk.js +435 -0
  66. package/dist/integrations/disk.ts +465 -0
  67. package/dist/integrations/generators/read.js +6 -6
  68. package/dist/integrations/generators/read.ts +4 -4
  69. package/dist/integrations/generators/vault.js +10 -11
  70. package/dist/integrations/generators/vault.ts +4 -5
  71. package/dist/{operations/http.d.ts → integrations/https.d.ts} +10 -6
  72. package/dist/integrations/https.js +170 -0
  73. package/dist/integrations/https.ts +216 -0
  74. package/dist/integrations/pglite.d.ts +26 -0
  75. package/dist/integrations/pglite.js +143 -0
  76. package/dist/integrations/pglite.ts +206 -0
  77. package/dist/integrations/secret.d.ts +8 -2
  78. package/dist/integrations/secret.js +3 -5
  79. package/dist/integrations/secret.ts +10 -6
  80. package/dist/integrations/target.d.ts +155 -14
  81. package/dist/integrations/target.js +171 -50
  82. package/dist/integrations/target.ts +377 -61
  83. package/dist/integrations/vault.d.ts +1 -0
  84. package/dist/integrations/vault.js +20 -5
  85. package/dist/integrations/vault.ts +17 -5
  86. package/dist/integrity/attach.js +8 -77
  87. package/dist/integrity/attach.ts +7 -76
  88. package/dist/integrity/check-threads.js +5 -5
  89. package/dist/integrity/check-threads.ts +2 -2
  90. package/dist/integrity/constraint.d.ts +0 -1
  91. package/dist/integrity/constraint.js +267 -366
  92. package/dist/integrity/constraint.ts +267 -375
  93. package/dist/integrity/generators/compiler.js +14 -17
  94. package/dist/integrity/generators/compiler.ts +10 -15
  95. package/dist/integrity/generators/gate.js +15 -15
  96. package/dist/integrity/generators/gate.ts +10 -10
  97. package/dist/integrity/generators/issues.js +7 -7
  98. package/dist/integrity/generators/publish.js +39 -57
  99. package/dist/integrity/generators/publish.ts +44 -55
  100. package/dist/integrity/generators/trial.d.ts +3 -3
  101. package/dist/integrity/generators/trial.js +133 -165
  102. package/dist/integrity/generators/trial.ts +145 -169
  103. package/dist/integrity/lint.js +9 -13
  104. package/dist/integrity/lint.ts +7 -10
  105. package/dist/integrity/lsp/bin.js +5 -4
  106. package/dist/integrity/lsp/bin.ts +3 -1
  107. package/dist/integrity/lsp/diagnostics.js +3 -3
  108. package/dist/integrity/lsp/diagnostics.ts +1 -1
  109. package/dist/integrity/lsp/lint.js +19 -30
  110. package/dist/integrity/lsp/lint.ts +16 -32
  111. package/dist/integrity/lsp/server.js +3 -3
  112. package/dist/integrity/slopnator.d.ts +3 -13
  113. package/dist/integrity/slopnator.js +59 -38
  114. package/dist/integrity/slopnator.ts +54 -38
  115. package/dist/integrity/slopnators/sl_broken_lineage.js +2 -2
  116. package/dist/integrity/slopnators/sl_declared_elsewhere.js +1 -1
  117. package/dist/integrity/slopnators/sl_derived_drift.js +4 -4
  118. package/dist/integrity/slopnators/sl_derived_drift.ts +1 -1
  119. package/dist/integrity/slopnators/sl_door_in_a_node.js +220 -0
  120. package/dist/integrity/slopnators/sl_door_in_a_node.ts +250 -0
  121. package/dist/integrity/slopnators/sl_inert_island.js +10 -8
  122. package/dist/integrity/slopnators/sl_inert_island.ts +8 -6
  123. package/dist/integrity/slopnators/sl_missing_row.js +6 -51
  124. package/dist/integrity/slopnators/sl_missing_row.ts +4 -50
  125. package/dist/integrity/slopnators/sl_no_barrel.js +1 -1
  126. package/dist/integrity/slopnators/sl_no_child_process.js +36 -18
  127. package/dist/integrity/slopnators/sl_no_child_process.ts +44 -18
  128. package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +80 -149
  129. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +95 -147
  130. package/dist/integrity/slopnators/sl_no_import_cycle.js +15 -4
  131. package/dist/integrity/slopnators/sl_no_import_cycle.ts +20 -4
  132. package/dist/integrity/slopnators/sl_no_such_command.js +2 -2
  133. package/dist/integrity/slopnators/sl_no_such_command.ts +1 -1
  134. package/dist/integrity/slopnators/sl_outside_an_operation.js +20 -0
  135. package/dist/integrity/slopnators/sl_outside_an_operation.ts +25 -0
  136. package/dist/integrity/slopnators/sl_screen_holds_only_declared.d.ts +2 -0
  137. package/dist/integrity/slopnators/sl_screen_holds_only_declared.js +61 -0
  138. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +70 -0
  139. package/dist/integrity/slopnators/sl_secret_literal.js +1 -33
  140. package/dist/integrity/slopnators/sl_secret_literal.ts +1 -33
  141. package/dist/integrity/slopnators/sl_stale_path.js +5 -22
  142. package/dist/integrity/slopnators/sl_stale_path.ts +4 -20
  143. package/dist/integrity/slopnators/sl_twice_meant.js +1 -1
  144. package/dist/integrity/slopnators/sl_twice_parented.js +29 -5
  145. package/dist/integrity/slopnators/sl_twice_parented.ts +28 -4
  146. package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +4 -1
  147. package/dist/integrity/slopnators/sl_undeclared_crossing.js +426 -62
  148. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +454 -70
  149. package/dist/integrity/slopnators/sl_undeclared_destination.js +59 -25
  150. package/dist/integrity/slopnators/sl_undeclared_destination.ts +69 -21
  151. package/dist/integrity/slopnators/sl_undeclared_list.d.ts +2 -0
  152. package/dist/integrity/slopnators/sl_undeclared_list.js +363 -0
  153. package/dist/integrity/slopnators/sl_undeclared_list.ts +358 -0
  154. package/dist/integrity/slopnators/sl_undeclared_node.js +1 -11
  155. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -10
  156. package/dist/integrity/slopnators/sl_undeclared_operation.js +111 -29
  157. package/dist/integrity/slopnators/sl_undeclared_operation.ts +134 -26
  158. package/dist/integrity/slopnators/sl_undeclared_variant.js +2 -2
  159. package/dist/integrity/slopnators/sl_undeclared_webhook.d.ts +2 -0
  160. package/dist/integrity/slopnators/sl_undeclared_webhook.js +90 -0
  161. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +108 -0
  162. package/dist/integrity/slopnators/sl_underived_route.d.ts +2 -0
  163. package/dist/integrity/slopnators/sl_underived_route.js +61 -0
  164. package/dist/integrity/slopnators/sl_underived_route.ts +72 -0
  165. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -3
  166. package/dist/integrity/slopnators/sl_ungoverned_island.ts +1 -1
  167. package/dist/integrity/slopnators/sl_unlisted_program.d.ts +2 -0
  168. package/dist/integrity/slopnators/sl_unlisted_program.js +39 -0
  169. package/dist/integrity/slopnators/sl_unlisted_program.ts +40 -0
  170. package/dist/integrity/slopnators/sl_unnamed_island.js +1 -1
  171. package/dist/integrity/slopnators/sl_unpaired_member.js +3 -3
  172. package/dist/integrity/slopnators/sl_unpaired_member.ts +1 -1
  173. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +4 -4
  174. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +1 -1
  175. package/dist/integrity/slopnators/sl_unprefixed_member.js +5 -31
  176. package/dist/integrity/slopnators/sl_unprefixed_member.ts +2 -28
  177. package/dist/integrity/slopnators/sl_unthreaded.js +11 -7
  178. package/dist/integrity/slopnators/sl_unthreaded.ts +6 -7
  179. package/dist/integrity/slopnators/sl_untranslated_key.js +2 -25
  180. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -25
  181. package/dist/integrity/slopnators/sl_unvaulted_secret.d.ts +2 -0
  182. package/dist/integrity/slopnators/sl_unvaulted_secret.js +107 -0
  183. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +132 -0
  184. package/dist/integrity/slopnators/sl_unversioned.js +6 -4
  185. package/dist/integrity/slopnators/sl_unversioned.ts +5 -3
  186. package/dist/integrity/sweep.d.ts +1 -0
  187. package/dist/integrity/sweep.js +105 -42
  188. package/dist/integrity/sweep.ts +115 -42
  189. package/dist/knowledge/attach.js +4 -3
  190. package/dist/knowledge/attach.ts +3 -2
  191. package/dist/knowledge/generators/docsgen.js +8 -6
  192. package/dist/knowledge/generators/docsgen.ts +4 -2
  193. package/dist/knowledge/generators/glossary.js +4 -4
  194. package/dist/knowledge/generators/glossary.ts +1 -1
  195. package/dist/operations/access.d.ts +23 -0
  196. package/dist/operations/access.js +28 -1
  197. package/dist/operations/access.ts +43 -0
  198. package/dist/operations/attach.js +33 -113
  199. package/dist/operations/attach.ts +51 -117
  200. package/dist/operations/capabilities.d.ts +14 -5
  201. package/dist/operations/capabilities.js +13 -4
  202. package/dist/operations/capabilities.ts +27 -8
  203. package/dist/operations/dispatch.d.ts +8 -2
  204. package/dist/operations/dispatch.js +128 -185
  205. package/dist/operations/dispatch.ts +138 -191
  206. package/dist/operations/error-results.d.ts +1 -1
  207. package/dist/operations/error-results.js +2 -2
  208. package/dist/operations/error-results.ts +2 -2
  209. package/dist/operations/generators/capability.d.ts +11 -1
  210. package/dist/operations/generators/capability.js +481 -224
  211. package/dist/operations/generators/capability.ts +562 -233
  212. package/dist/operations/generators/compile.js +66 -39
  213. package/dist/operations/generators/compile.ts +78 -39
  214. package/dist/operations/generators/permits.js +11 -12
  215. package/dist/operations/generators/permits.ts +3 -4
  216. package/dist/operations/generators/seed.d.ts +1 -0
  217. package/dist/operations/generators/seed.js +19 -17
  218. package/dist/operations/generators/seed.ts +22 -14
  219. package/dist/operations/generators/trial.d.ts +6 -8
  220. package/dist/operations/generators/trial.js +200 -70
  221. package/dist/operations/generators/trial.ts +230 -93
  222. package/dist/operations/nodes.d.ts +15 -16
  223. package/dist/operations/nodes.ts +16 -17
  224. package/dist/operations/operations.d.ts +40 -4
  225. package/dist/operations/operations.js +1 -0
  226. package/dist/operations/operations.ts +61 -4
  227. package/dist/operations/result.d.ts +2 -2
  228. package/dist/operations/result.ts +2 -2
  229. package/package.json +1 -4
  230. package/dist/core/answers.d.ts +0 -171
  231. package/dist/core/answers.js +0 -789
  232. package/dist/core/answers.ts +0 -999
  233. package/dist/core/client.d.ts +0 -63
  234. package/dist/core/client.js +0 -249
  235. package/dist/core/client.ts +0 -289
  236. package/dist/core/files.js +0 -171
  237. package/dist/core/files.ts +0 -222
  238. package/dist/integrations/module.d.ts +0 -6
  239. package/dist/integrations/module.js +0 -153
  240. package/dist/integrations/module.ts +0 -191
  241. package/dist/integrity/slopnators/sl_unkept_rows.js +0 -104
  242. package/dist/integrity/slopnators/sl_unkept_rows.ts +0 -111
  243. package/dist/integrity/slopnators/sl_unseamed_reach.js +0 -73
  244. package/dist/integrity/slopnators/sl_unseamed_reach.ts +0 -79
  245. package/dist/operations/cost.d.ts +0 -14
  246. package/dist/operations/cost.js +0 -37
  247. package/dist/operations/cost.ts +0 -58
  248. package/dist/operations/errors/errors.d.ts +0 -52
  249. package/dist/operations/http.js +0 -124
  250. package/dist/operations/http.ts +0 -169
  251. package/dist/operations/memory.d.ts +0 -104
  252. package/dist/operations/memory.js +0 -365
  253. package/dist/operations/memory.ts +0 -470
  254. package/dist/operations/metering.d.ts +0 -55
  255. package/dist/operations/metering.js +0 -247
  256. package/dist/operations/metering.ts +0 -328
  257. package/dist/operations/queue-policy.d.ts +0 -3
  258. package/dist/operations/queue-policy.js +0 -8
  259. package/dist/operations/queue-policy.ts +0 -10
  260. package/dist/operations/seams.d.ts +0 -239
  261. package/dist/operations/seams.js +0 -45
  262. package/dist/operations/seams.ts +0 -364
  263. package/dist/operations/store.d.ts +0 -14
  264. package/dist/operations/store.js +0 -1
  265. package/dist/operations/store.ts +0 -29
  266. /package/dist/{operations → core}/errors/classes.d.ts +0 -0
  267. /package/dist/{operations → core}/errors/classes.js +0 -0
  268. /package/dist/{operations → core}/errors/classes.ts +0 -0
  269. /package/dist/integrity/slopnators/{sl_unkept_rows.d.ts → sl_door_in_a_node.d.ts} +0 -0
  270. /package/dist/integrity/slopnators/{sl_unseamed_reach.d.ts → sl_outside_an_operation.d.ts} +0 -0
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env node
2
- import { type Kind, type Listed, type Measured } from './files.ts';
3
- export type Order = {
4
- readonly order: 'kind';
5
- readonly at: string;
6
- } | {
7
- readonly order: 'read';
8
- readonly at: string;
9
- } | {
10
- readonly order: 'list';
11
- readonly at: string;
12
- } | {
13
- readonly order: 'stat';
14
- readonly at: string;
15
- } | {
16
- readonly order: 'remove';
17
- readonly at: string;
18
- } | {
19
- readonly order: 'real';
20
- readonly at: string;
21
- } | {
22
- readonly order: 'write';
23
- readonly at: string;
24
- readonly said: string;
25
- } | {
26
- readonly order: 'fetch';
27
- readonly at: string;
28
- };
29
- export type Answered = {
30
- readonly answered: 'kind';
31
- readonly held: Kind;
32
- } | {
33
- readonly answered: 'read';
34
- readonly said: string;
35
- } | {
36
- readonly answered: 'list';
37
- readonly held: readonly Listed[];
38
- } | {
39
- readonly answered: 'stat';
40
- readonly held: Measured;
41
- } | {
42
- readonly answered: 'written';
43
- } | {
44
- readonly answered: 'removed';
45
- } | {
46
- readonly answered: 'real';
47
- readonly at: string;
48
- } | {
49
- readonly answered: 'fetched';
50
- readonly said: string;
51
- } | {
52
- readonly answered: 'refused';
53
- readonly because: string;
54
- };
55
- export declare function carry(one: Order): Answered;
56
- export interface Turn {
57
- readonly of: string;
58
- readonly orders?: readonly Order[];
59
- readonly say?: string;
60
- readonly done?: boolean;
61
- }
62
- export declare function talking(at: string, key: string, message: string): Promise<number>;
63
- export declare function run(said: readonly string[]): Promise<number>;
@@ -1,249 +0,0 @@
1
- #!/usr/bin/env node
2
- import { dirname, isAbsolute, relative, resolve } from 'node:path';
3
- import { argv, cwd, env, stderr, stdin, stdout } from 'node:process';
4
- import { createInterface, emitKeypressEvents } from 'node:readline';
5
- import { installing, kind, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "./files.js";
6
- import { exit, invoked } from "./invoked.js";
7
- import { THING } from "./scope.js";
8
- const HERE = cwd();
9
- const REASONS = {
10
- ENOENT: 'is not there',
11
- EACCES: 'is there, and this machine does not allow it',
12
- EPERM: 'is there, and this machine does not allow it',
13
- EISDIR: 'is a folder, and that order is for a file',
14
- ENOTDIR: 'has a file part-way along its path where a folder was expected',
15
- ENOTEMPTY: 'still holds something',
16
- };
17
- const HOME = (() => {
18
- try {
19
- return realpathSync(HERE);
20
- }
21
- catch {
22
- return HERE;
23
- }
24
- })();
25
- function deepest(at) {
26
- let found = at;
27
- while (kind(found) === 'none') {
28
- const up = dirname(found);
29
- if (up === found)
30
- return found;
31
- found = up;
32
- }
33
- return found;
34
- }
35
- function inside(at) {
36
- let real;
37
- try {
38
- real = realpathSync(deepest(resolve(HERE, at)));
39
- }
40
- catch {
41
- return false;
42
- }
43
- const step = relative(HOME, real);
44
- return step === '' || (!step.startsWith('..') && !isAbsolute(step));
45
- }
46
- function rootly(at) {
47
- const asked = resolve(HERE, at);
48
- if (kind(asked) === 'none')
49
- return false;
50
- try {
51
- return realpathSync(asked) === HOME;
52
- }
53
- catch {
54
- return false;
55
- }
56
- }
57
- const LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);
58
- function local(at) {
59
- try {
60
- return LOOPBACK.has(new URL(at).hostname);
61
- }
62
- catch {
63
- return false;
64
- }
65
- }
66
- function why(at, thrown) {
67
- const held = thrown instanceof Error ? Reflect.get(thrown, 'code') : undefined;
68
- const said = typeof held === 'string' ? REASONS[held] : undefined;
69
- if (said !== undefined)
70
- return `${at} ${said}.`;
71
- return `${at} could not be reached — ${thrown instanceof Error ? thrown.message : String(thrown)}.`;
72
- }
73
- export function carry(one) {
74
- if (!inside(one.at)) {
75
- return {
76
- answered: 'refused',
77
- because: `${one.at} is outside ${HERE} once its links are followed, and this answers only for what is really inside the repository it was run in.`,
78
- };
79
- }
80
- if (one.order === 'remove' && rootly(one.at)) {
81
- return {
82
- answered: 'refused',
83
- because: `${one.at} is the repository this was run in, and this carries orders inside it rather than taking it away.`,
84
- };
85
- }
86
- try {
87
- if (one.order === 'kind')
88
- return { answered: 'kind', held: kind(one.at) };
89
- if (one.order === 'read')
90
- return { answered: 'read', said: readFileSync(one.at) };
91
- if (one.order === 'list') {
92
- const held = readdirSync(one.at, { withFileTypes: true }).map((each) => ({
93
- name: each.name,
94
- kind: each.isFile() ? 'file' : each.isDirectory() ? 'folder' : 'other',
95
- }));
96
- return { answered: 'list', held };
97
- }
98
- if (one.order === 'stat') {
99
- const held = statSync(one.at);
100
- return { answered: 'stat', held: { size: held.size, modified: held.mtimeMs } };
101
- }
102
- if (one.order === 'remove') {
103
- rmSync(one.at, { recursive: true, force: true });
104
- return { answered: 'removed' };
105
- }
106
- if (one.order === 'real')
107
- return { answered: 'real', at: realpathSync(one.at) };
108
- if (one.order === 'fetch')
109
- return { answered: 'fetched', said: installing(HERE) };
110
- writeFileSync(one.at, one.said);
111
- return { answered: 'written' };
112
- }
113
- catch (thrown) {
114
- return { answered: 'refused', because: why(one.at, thrown) };
115
- }
116
- }
117
- const AT = 'THREAD_SERVER';
118
- const KEY = 'THREAD_KEY';
119
- const QUIET = 100;
120
- async function sending(at, key, body) {
121
- const said = await fetch(at, {
122
- method: 'POST',
123
- headers: { 'content-type': 'application/json', authorization: `Bearer ${key}` },
124
- body: JSON.stringify(body),
125
- });
126
- if (said.status === 401 || said.status === 403)
127
- throw new Error(`the key in ${KEY} is not one ${at} accepts`);
128
- if (!said.ok)
129
- throw new Error(`${at} answered ${String(said.status)}`);
130
- return (await said.json());
131
- }
132
- export async function talking(at, key, message) {
133
- let turn = await sending(at, key, { said: message });
134
- let silent = 0;
135
- for (;;) {
136
- if (turn.say !== undefined && turn.say !== '')
137
- stdout.write(`${turn.say}\n`);
138
- if (turn.done === true)
139
- return 0;
140
- const orders = turn.orders ?? [];
141
- silent = orders.length === 0 && (turn.say === undefined || turn.say === '') ? silent + 1 : 0;
142
- if (silent >= QUIET)
143
- break;
144
- turn = await sending(at, key, { of: turn.of, answered: orders.map(carry) });
145
- }
146
- stderr.write(`${THING}: nothing has come back from ${at} for ${String(QUIET)} exchanges, so this stopped waiting. What is already\n` +
147
- `in this repository stands, and nothing half-written was left behind — every change is one order, carried\n` +
148
- `and answered before the next one is asked for.\n` +
149
- `This only ever gives up on silence: a server that keeps asking for files never reaches it, so a run that is\n` +
150
- `going nowhere and still talking is one you stop with ctrl-c.\n`);
151
- return 1;
152
- }
153
- const PROMPT = '> ';
154
- async function chatting(at, key) {
155
- stdout.write(THING +
156
- ': talking to ' +
157
- at +
158
- '. Say what you want built, in a sentence — there is no command here to look up.\n' +
159
- 'Type while a turn is running and it is held until that turn ends, so you can line several up.\n' +
160
- 'Press Esc to stop a turn where it is. What you say after that carries on the same conversation with\n' +
161
- 'everything before it intact, so Esc and then typing is how you cut in with something new.\n' +
162
- 'Ctrl-C to leave.\n\n');
163
- const queued = [];
164
- let busy = false;
165
- const held = createInterface({ input: stdin, output: stdout, prompt: PROMPT });
166
- emitKeypressEvents(stdin);
167
- stdin.on('keypress', (...said) => {
168
- const one = said[1];
169
- if (busy && one !== undefined && one.name === 'escape')
170
- void sending(at, key, { stop: true }).catch((thrown) => {
171
- stderr.write(THING + ': ' + at + ' would not stop it — ' + (thrown instanceof Error ? thrown.message : String(thrown)) + '. The turn is still running.\n');
172
- });
173
- });
174
- const next = async () => {
175
- const one = queued.shift();
176
- if (one === undefined) {
177
- busy = false;
178
- held.prompt();
179
- return;
180
- }
181
- busy = true;
182
- try {
183
- await talking(at, key, one);
184
- }
185
- catch (thrown) {
186
- stderr.write(THING + ': ' + at + ' could not answer — ' + (thrown instanceof Error ? thrown.message : String(thrown)) + '. Nothing was written.\n');
187
- }
188
- await next();
189
- };
190
- held.on('line', (line) => {
191
- const one = line.trim();
192
- if (one.length === 0) {
193
- if (!busy)
194
- held.prompt();
195
- return;
196
- }
197
- queued.push(one);
198
- if (busy) {
199
- stdout.write(THING + ': queued — it goes next.\n');
200
- return;
201
- }
202
- void next();
203
- });
204
- held.prompt();
205
- return await new Promise((settle) => {
206
- held.on('close', () => {
207
- settle(0);
208
- });
209
- });
210
- }
211
- function pointed() {
212
- const at = env[AT];
213
- if (at === undefined || at.length === 0) {
214
- stderr.write(`${THING}: there is nowhere to send that yet. This copy has no server to answer it — ${AT} names one, and\n` +
215
- `nothing here is set to one.\n`);
216
- return undefined;
217
- }
218
- if (!local(at)) {
219
- stderr.write(`${THING}: ${AT} names ${at}, which is not on this machine, and this only ever talks to a server running\n` +
220
- `here. Set ${AT} to a http://localhost address and say it again. Nothing was sent.\n`);
221
- return undefined;
222
- }
223
- return at;
224
- }
225
- export async function run(said) {
226
- const at = pointed();
227
- if (at === undefined)
228
- return 1;
229
- const key = env[KEY] ?? '';
230
- if (said.length === 0)
231
- return await chatting(at, key);
232
- const message = said.length === 1 ? (said[0] ?? '') : '';
233
- if (!message.includes(' ')) {
234
- stderr.write(`${THING}: there is no \`${said.join(' ')}\` here, and no other command to find instead. This takes one\n` +
235
- `sentence, in quotes, and nothing else — ${THING} "take payments with stripe". Say what you want and it\n` +
236
- `is answered where the work happens, never on this machine.\n`);
237
- return 1;
238
- }
239
- try {
240
- return await talking(at, key, message);
241
- }
242
- catch (thrown) {
243
- stderr.write(`${THING}: ${at} could not answer — ${thrown instanceof Error ? thrown.message : String(thrown)}. Nothing was\n` +
244
- `written, so this repository is as it was.\n`);
245
- return 1;
246
- }
247
- }
248
- if (invoked(import.meta.url))
249
- exit(await run(argv.slice(2)));
@@ -1,289 +0,0 @@
1
- #!/usr/bin/env node
2
- import { dirname, isAbsolute, relative, resolve } from 'node:path'
3
- import { argv, cwd, env, stderr, stdin, stdout } from 'node:process'
4
- import { createInterface, emitKeypressEvents } from 'node:readline'
5
- import { installing, kind, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync, type Kind, type Listed, type Measured } from './files.ts'
6
- import { exit, invoked } from './invoked.ts'
7
- import { THING } from './scope.ts'
8
-
9
- export type Order =
10
- | { readonly order: 'kind'; readonly at: string }
11
- | { readonly order: 'read'; readonly at: string }
12
- | { readonly order: 'list'; readonly at: string }
13
- | { readonly order: 'stat'; readonly at: string }
14
- | { readonly order: 'remove'; readonly at: string }
15
- | { readonly order: 'real'; readonly at: string }
16
- | { readonly order: 'write'; readonly at: string; readonly said: string }
17
- | { readonly order: 'fetch'; readonly at: string }
18
-
19
- export type Answered =
20
- | { readonly answered: 'kind'; readonly held: Kind }
21
- | { readonly answered: 'read'; readonly said: string }
22
- | { readonly answered: 'list'; readonly held: readonly Listed[] }
23
- | { readonly answered: 'stat'; readonly held: Measured }
24
- | { readonly answered: 'written' }
25
- | { readonly answered: 'removed' }
26
- | { readonly answered: 'real'; readonly at: string }
27
- | { readonly answered: 'fetched'; readonly said: string }
28
- | { readonly answered: 'refused'; readonly because: string }
29
-
30
- const HERE = cwd()
31
-
32
- const REASONS: Readonly<Record<string, string>> = {
33
- ENOENT: 'is not there',
34
- EACCES: 'is there, and this machine does not allow it',
35
- EPERM: 'is there, and this machine does not allow it',
36
- EISDIR: 'is a folder, and that order is for a file',
37
- ENOTDIR: 'has a file part-way along its path where a folder was expected',
38
- ENOTEMPTY: 'still holds something',
39
- }
40
-
41
- const HOME = ((): string => {
42
- try {
43
- return realpathSync(HERE)
44
- } catch {
45
- return HERE
46
- }
47
- })()
48
-
49
- function deepest(at: string): string {
50
- let found = at
51
- while (kind(found) === 'none') {
52
- const up = dirname(found)
53
- if (up === found) return found
54
- found = up
55
- }
56
- return found
57
- }
58
-
59
- function inside(at: string): boolean {
60
- let real: string
61
- try {
62
- real = realpathSync(deepest(resolve(HERE, at)))
63
- } catch {
64
- return false
65
- }
66
- const step = relative(HOME, real)
67
- return step === '' || (!step.startsWith('..') && !isAbsolute(step))
68
- }
69
-
70
- function rootly(at: string): boolean {
71
- const asked = resolve(HERE, at)
72
- if (kind(asked) === 'none') return false
73
- try {
74
- return realpathSync(asked) === HOME
75
- } catch {
76
- return false
77
- }
78
- }
79
-
80
- const LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]', '::1'])
81
-
82
- function local(at: string): boolean {
83
- try {
84
- return LOOPBACK.has(new URL(at).hostname)
85
- } catch {
86
- return false
87
- }
88
- }
89
-
90
- function why(at: string, thrown: unknown): string {
91
- const held: unknown = thrown instanceof Error ? Reflect.get(thrown, 'code') : undefined
92
- const said = typeof held === 'string' ? REASONS[held] : undefined
93
- if (said !== undefined) return `${at} ${said}.`
94
- return `${at} could not be reached — ${thrown instanceof Error ? thrown.message : String(thrown)}.`
95
- }
96
-
97
- export function carry(one: Order): Answered {
98
- if (!inside(one.at)) {
99
- return {
100
- answered: 'refused',
101
- because: `${one.at} is outside ${HERE} once its links are followed, and this answers only for what is really inside the repository it was run in.`,
102
- }
103
- }
104
- if (one.order === 'remove' && rootly(one.at)) {
105
- return {
106
- answered: 'refused',
107
- because: `${one.at} is the repository this was run in, and this carries orders inside it rather than taking it away.`,
108
- }
109
- }
110
- try {
111
- if (one.order === 'kind') return { answered: 'kind', held: kind(one.at) }
112
- if (one.order === 'read') return { answered: 'read', said: readFileSync(one.at) }
113
- if (one.order === 'list') {
114
- const held = readdirSync(one.at, { withFileTypes: true }).map((each) => ({
115
- name: each.name,
116
- kind: each.isFile() ? ('file' as const) : each.isDirectory() ? ('folder' as const) : ('other' as const),
117
- }))
118
- return { answered: 'list', held }
119
- }
120
- if (one.order === 'stat') {
121
- const held = statSync(one.at)
122
- return { answered: 'stat', held: { size: held.size, modified: held.mtimeMs } }
123
- }
124
- if (one.order === 'remove') {
125
- rmSync(one.at, { recursive: true, force: true })
126
- return { answered: 'removed' }
127
- }
128
- if (one.order === 'real') return { answered: 'real', at: realpathSync(one.at) }
129
- if (one.order === 'fetch') return { answered: 'fetched', said: installing(HERE) }
130
- writeFileSync(one.at, one.said)
131
- return { answered: 'written' }
132
- } catch (thrown) {
133
- return { answered: 'refused', because: why(one.at, thrown) }
134
- }
135
- }
136
-
137
- export interface Turn {
138
- readonly of: string
139
- readonly orders?: readonly Order[]
140
- readonly say?: string
141
- readonly done?: boolean
142
- }
143
-
144
- const AT = 'THREAD_SERVER'
145
-
146
- const KEY = 'THREAD_KEY'
147
-
148
- const QUIET = 100
149
-
150
- async function sending(at: string, key: string, body: unknown): Promise<Turn> {
151
- const said = await fetch(at, {
152
- method: 'POST',
153
- headers: { 'content-type': 'application/json', authorization: `Bearer ${key}` },
154
- body: JSON.stringify(body),
155
- })
156
- if (said.status === 401 || said.status === 403) throw new Error(`the key in ${KEY} is not one ${at} accepts`)
157
- if (!said.ok) throw new Error(`${at} answered ${String(said.status)}`)
158
- return (await said.json()) as Turn
159
- }
160
-
161
- export async function talking(at: string, key: string, message: string): Promise<number> {
162
- let turn = await sending(at, key, { said: message })
163
- let silent = 0
164
- for (;;) {
165
- if (turn.say !== undefined && turn.say !== '') stdout.write(`${turn.say}\n`)
166
- if (turn.done === true) return 0
167
- const orders = turn.orders ?? []
168
- silent = orders.length === 0 && (turn.say === undefined || turn.say === '') ? silent + 1 : 0
169
- if (silent >= QUIET) break
170
- turn = await sending(at, key, { of: turn.of, answered: orders.map(carry) })
171
- }
172
- stderr.write(
173
- `${THING}: nothing has come back from ${at} for ${String(QUIET)} exchanges, so this stopped waiting. What is already\n` +
174
- `in this repository stands, and nothing half-written was left behind — every change is one order, carried\n` +
175
- `and answered before the next one is asked for.\n` +
176
- `This only ever gives up on silence: a server that keeps asking for files never reaches it, so a run that is\n` +
177
- `going nowhere and still talking is one you stop with ctrl-c.\n`,
178
- )
179
- return 1
180
- }
181
-
182
- const PROMPT = '> '
183
-
184
- async function chatting(at: string, key: string): Promise<number> {
185
- stdout.write(
186
- THING +
187
- ': talking to ' +
188
- at +
189
- '. Say what you want built, in a sentence — there is no command here to look up.\n' +
190
- 'Type while a turn is running and it is held until that turn ends, so you can line several up.\n' +
191
- 'Press Esc to stop a turn where it is. What you say after that carries on the same conversation with\n' +
192
- 'everything before it intact, so Esc and then typing is how you cut in with something new.\n' +
193
- 'Ctrl-C to leave.\n\n',
194
- )
195
- const queued: string[] = []
196
- let busy = false
197
- const held = createInterface({ input: stdin, output: stdout, prompt: PROMPT })
198
- emitKeypressEvents(stdin)
199
- stdin.on('keypress', (...said: readonly unknown[]) => {
200
- const one = said[1] as { readonly name?: string } | undefined
201
- if (busy && one !== undefined && one.name === 'escape')
202
- void sending(at, key, { stop: true }).catch((thrown: unknown) => {
203
- stderr.write(
204
- THING + ': ' + at + ' would not stop it — ' + (thrown instanceof Error ? thrown.message : String(thrown)) + '. The turn is still running.\n',
205
- )
206
- })
207
- })
208
- const next = async (): Promise<void> => {
209
- const one = queued.shift()
210
- if (one === undefined) {
211
- busy = false
212
- held.prompt()
213
- return
214
- }
215
- busy = true
216
- try {
217
- await talking(at, key, one)
218
- } catch (thrown) {
219
- stderr.write(THING + ': ' + at + ' could not answer — ' + (thrown instanceof Error ? thrown.message : String(thrown)) + '. Nothing was written.\n')
220
- }
221
- await next()
222
- }
223
- held.on('line', (line: string) => {
224
- const one = line.trim()
225
- if (one.length === 0) {
226
- if (!busy) held.prompt()
227
- return
228
- }
229
- queued.push(one)
230
- if (busy) {
231
- stdout.write(THING + ': queued — it goes next.\n')
232
- return
233
- }
234
- void next()
235
- })
236
- held.prompt()
237
- return await new Promise<number>((settle) => {
238
- held.on('close', () => {
239
- settle(0)
240
- })
241
- })
242
- }
243
-
244
- function pointed(): string | undefined {
245
- const at = env[AT]
246
- if (at === undefined || at.length === 0) {
247
- stderr.write(
248
- `${THING}: there is nowhere to send that yet. This copy has no server to answer it — ${AT} names one, and\n` +
249
- `nothing here is set to one.\n`,
250
- )
251
- return undefined
252
- }
253
- if (!local(at)) {
254
- stderr.write(
255
- `${THING}: ${AT} names ${at}, which is not on this machine, and this only ever talks to a server running\n` +
256
- `here. Set ${AT} to a http://localhost address and say it again. Nothing was sent.\n`,
257
- )
258
- return undefined
259
- }
260
- return at
261
- }
262
-
263
- export async function run(said: readonly string[]): Promise<number> {
264
- const at = pointed()
265
- if (at === undefined) return 1
266
- const key = env[KEY] ?? ''
267
- if (said.length === 0) return await chatting(at, key)
268
- const message = said.length === 1 ? (said[0] ?? '') : ''
269
- if (!message.includes(' ')) {
270
- stderr.write(
271
- `${THING}: there is no \`${said.join(' ')}\` here, and no other command to find instead. This takes one\n` +
272
- `sentence, in quotes, and nothing else — ${THING} "take payments with stripe". Say what you want and it\n` +
273
- `is answered where the work happens, never on this machine.\n`,
274
- )
275
- return 1
276
- }
277
-
278
- try {
279
- return await talking(at, key, message)
280
- } catch (thrown) {
281
- stderr.write(
282
- `${THING}: ${at} could not answer — ${thrown instanceof Error ? thrown.message : String(thrown)}. Nothing was\n` +
283
- `written, so this repository is as it was.\n`,
284
- )
285
- return 1
286
- }
287
- }
288
-
289
- if (invoked(import.meta.url)) exit(await run(argv.slice(2)))