@studiometa/trafic-agent 0.1.49 → 0.1.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +158 -13
  2. package/dist/cli.js +134 -14
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.d.ts +7 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +3 -3
  7. package/dist/{server-DZAo8Clg.js → server-Cfk-O4DJ.js} +406 -30
  8. package/dist/server-Cfk-O4DJ.js.map +1 -0
  9. package/dist/server.d.ts +26 -2
  10. package/dist/server.d.ts.map +1 -1
  11. package/dist/server.js +2 -2
  12. package/dist/setup/agent.d.ts +11 -1
  13. package/dist/setup/agent.d.ts.map +1 -1
  14. package/dist/setup/migrations/0020__backup_dir.d.ts +23 -0
  15. package/dist/setup/migrations/0020__backup_dir.d.ts.map +1 -0
  16. package/dist/setup/migrations/index.d.ts.map +1 -1
  17. package/dist/{setup-DWgb6dAD.js → setup-CiWIirVb.js} +827 -209
  18. package/dist/setup-CiWIirVb.js.map +1 -0
  19. package/dist/tasks/access-logs.d.ts +8 -0
  20. package/dist/tasks/access-logs.d.ts.map +1 -0
  21. package/dist/tasks/backup.d.ts +48 -0
  22. package/dist/tasks/backup.d.ts.map +1 -0
  23. package/dist/tasks/destroy-project.d.ts +71 -0
  24. package/dist/tasks/destroy-project.d.ts.map +1 -0
  25. package/dist/tasks/destroy-scheduled.d.ts +41 -0
  26. package/dist/tasks/destroy-scheduled.d.ts.map +1 -0
  27. package/dist/tasks/running-limit.d.ts +54 -0
  28. package/dist/tasks/running-limit.d.ts.map +1 -0
  29. package/dist/tasks/stop-idle.d.ts +13 -2
  30. package/dist/tasks/stop-idle.d.ts.map +1 -1
  31. package/dist/types.d.ts +41 -2
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/utils/config.d.ts +6 -0
  34. package/dist/utils/config.d.ts.map +1 -1
  35. package/dist/utils/db.d.ts +76 -1
  36. package/dist/utils/db.d.ts.map +1 -1
  37. package/dist/utils/ddev.d.ts +12 -5
  38. package/dist/utils/ddev.d.ts.map +1 -1
  39. package/dist/utils/paths.d.ts +3 -0
  40. package/dist/utils/paths.d.ts.map +1 -0
  41. package/package.json +1 -1
  42. package/dist/server-DZAo8Clg.js.map +0 -1
  43. package/dist/setup-DWgb6dAD.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"destroy-scheduled.d.ts","sourceRoot":"","sources":["../../src/tasks/destroy-scheduled.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,KAAK,EACX,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,EACtD,GAAG,GAAE,KAAmB,GACvB,IAAI,CAkBN;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,WAAW,EACnB,GAAG,GAAE,KAAmB,EACxB,GAAG,GAAE,UAA2B,GAC/B,OAAO,CAAC,IAAI,CAAC,CAyCf;AAOD;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,OAAO,CAYlF"}
@@ -0,0 +1,54 @@
1
+ import { getProject } from "../utils/db.js";
2
+ import { type DdevRunner } from "../utils/ddev.js";
3
+ import type { DdevProject } from "../types.js";
4
+ /**
5
+ * What the limit needs from the outside world. Injected so a test can set
6
+ * the DDEV state and record the stops without a DDEV install.
7
+ */
8
+ export interface RunningLimitDeps {
9
+ listProjects: () => Promise<DdevProject[]>;
10
+ stopProject: (name: string) => Promise<boolean>;
11
+ getProject: typeof getProject;
12
+ setProjectStatus: (name: string, status: "running" | "stopped") => void;
13
+ updateProjectAccess: (name: string) => void;
14
+ }
15
+ /**
16
+ * The real dependencies: the agent database, and DDEV through `run`. The
17
+ * default runner is the real one.
18
+ */
19
+ export declare function ddevRunningLimitDeps(run?: DdevRunner): RunningLimitDeps;
20
+ export interface RunningLimitOptions {
21
+ /** max_running_projects: 0 means no limit */
22
+ max: number;
23
+ /** Whether a project's own config says never to stop it */
24
+ neverStops: (name: string) => boolean;
25
+ }
26
+ /**
27
+ * Stop the least recently used projects until `max` or fewer run.
28
+ *
29
+ * A project runs when `ddev list` shows it up, or when the agent is starting
30
+ * it (status "starting" in the database). `ddev list` is the source because
31
+ * `trafic deploy` runs `ddev start` itself, so the database does not know
32
+ * about every running project. The projects the agent is starting count too,
33
+ * because DDEV shows them stopped until their containers exist: the waiting
34
+ * page marks its project "starting" before it calls this, so that project
35
+ * counts once whether DDEV shows it up or not, and calls made for starts at
36
+ * the same time each see all of them.
37
+ *
38
+ * Because of deploys, the database is first brought in line with DDEV. A
39
+ * project that runs while the database says it is stopped (or has no record
40
+ * of it) was started outside the agent, most likely by a deploy. Its last
41
+ * access is then old or missing, which would make it the first one stopped,
42
+ * right after the deploy. It is recorded as accessed now instead. A project
43
+ * the database says runs while DDEV says it does not was stopped outside the
44
+ * agent, and is recorded as stopped, so its next start is seen too.
45
+ *
46
+ * Projects with `idle_timeout: never`, projects the agent is starting and
47
+ * projects `trafic-agent destroy` is removing (status "destroying": its
48
+ * backup starts a stopped project) are never stopped. When no other project
49
+ * can be stopped, a warning is logged and the server stays over the limit.
50
+ *
51
+ * Returns the names of the projects it stopped.
52
+ */
53
+ export declare function enforceRunningLimit(options: RunningLimitOptions, deps?: RunningLimitDeps): Promise<string[]>;
54
+ //# sourceMappingURL=running-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"running-limit.d.ts","sourceRoot":"","sources":["../../src/tasks/running-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAGX,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AAE9D;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,SAAS,KAAK,IAAI,CAAC;IACxE,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,UAA2B,GAC/B,gBAAgB,CAQlB;AAOD,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,2DAA2D;IAC3D,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACvC;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,GAAE,gBAAyC,GAC9C,OAAO,CAAC,MAAM,EAAE,CAAC,CA2DnB"}
@@ -1,8 +1,19 @@
1
+ import { type DdevRunner } from "../utils/ddev.js";
1
2
  import type { AgentConfig } from "../types.js";
2
3
  /**
3
4
  * Stop idle projects that haven't been accessed recently
4
- * Respects per-project idle_timeout settings from .ddev/config.trafic.yaml
5
+ * A project's idle_timeout in .ddev/config.trafic.yaml replaces the global
6
+ * one, shorter or longer
7
+ *
8
+ * Only projects with the status "running" are idle: a project the agent is
9
+ * starting or removing is never stopped here. A start or a removal whose
10
+ * process was killed is set back to "running" first, so its project is not
11
+ * left up for good. Before that, the records of projects that DDEV no longer
12
+ * lists are deleted, so the sweep does not ask DDEV about them.
13
+ *
14
+ * Then brings the server back under max_running_projects: a deploy runs
15
+ * `ddev start` without asking the agent, so only the sweep sees it.
5
16
  */
6
- export declare function stopIdleProjects(config: AgentConfig): Promise<void>;
17
+ export declare function stopIdleProjects(config: AgentConfig, run?: DdevRunner): Promise<void>;
7
18
  export declare function startIdleScheduler(config: AgentConfig): NodeJS.Timeout;
8
19
  //# sourceMappingURL=stop-idle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stop-idle.d.ts","sourceRoot":"","sources":["../../src/tasks/stop-idle.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAkDzE;AAUD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,OAAO,CActE"}
1
+ {"version":3,"file":"stop-idle.d.ts","sourceRoot":"","sources":["../../src/tasks/stop-idle.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,WAAW,EACnB,GAAG,GAAE,UAA2B,GAC/B,OAAO,CAAC,IAAI,CAAC,CA4Df;AAUD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,OAAO,CActE"}
package/dist/types.d.ts CHANGED
@@ -16,10 +16,26 @@ export interface AgentConfig {
16
16
  idleTimeout: string;
17
17
  /** Interval for checking idle projects (e.g., "5m") */
18
18
  idleCheckInterval: string;
19
+ /** Most projects running at the same time (0 = no limit) */
20
+ maxRunningProjects: number;
19
21
  /** Auth configuration */
20
22
  auth: AuthConfig;
21
23
  /** TLS configuration */
22
24
  tls: TlsConfig;
25
+ /** Backup configuration */
26
+ backup: BackupConfig;
27
+ }
28
+ /**
29
+ * Backup configuration.
30
+ *
31
+ * A backup is a directory `<dir>/<project>/<timestamp>` that holds the
32
+ * database dump and an archive of the upload directories.
33
+ */
34
+ export interface BackupConfig {
35
+ /** Directory that holds the backups */
36
+ dir: string;
37
+ /** Days to keep a backup before the agent removes it */
38
+ retainDays: number;
23
39
  }
24
40
  /**
25
41
  * TLS configuration.
@@ -85,12 +101,13 @@ export interface AuthRule {
85
101
  */
86
102
  export interface DdevProject {
87
103
  name: string;
88
- status: "running" | "stopped" | "paused" | "unhealthy";
104
+ status: "running" | "starting" | "stopped" | "paused" | "unhealthy";
89
105
  appRoot: string;
90
106
  httpURLs: string[];
91
107
  httpsURLs: string[];
92
108
  type: string;
93
109
  phpVersion?: string;
110
+ /** Database type, or undefined when the project omits the db container */
94
111
  dbType?: string;
95
112
  }
96
113
  /**
@@ -99,7 +116,29 @@ export interface DdevProject {
99
116
  export interface ProjectRecord {
100
117
  name: string;
101
118
  lastAccess: number;
102
- status: "running" | "stopped" | "starting";
119
+ /**
120
+ * "starting": the waiting page is starting it. "destroying": trafic-agent
121
+ * destroy is removing it, see destroyProject. Both are set back when their
122
+ * process is gone, see releaseStaleStatuses.
123
+ */
124
+ status: "running" | "stopped" | "starting" | "destroying";
125
+ }
126
+ /**
127
+ * A removal scheduled with `trafic-agent destroy --delay`
128
+ */
129
+ export interface ScheduledDestroy {
130
+ name: string;
131
+ /** When the removal is due, as a Unix timestamp in milliseconds */
132
+ destroyAt: number;
133
+ /** Whether to back the project up before it is removed */
134
+ backup: boolean;
135
+ /** Time limit of the backup (`--timeout`), when one was given */
136
+ timeoutMs?: number;
137
+ /**
138
+ * Whether the sweep has started to remove the project, after its backup:
139
+ * a cancel is then too late, and a retry does not back it up again
140
+ */
141
+ removing: boolean;
103
142
  }
104
143
  /**
105
144
  * Access log entry
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,12 @@ import type { AgentConfig } from "../types.js";
3
3
  * Parse a duration string (e.g., "30m", "1h", "2h30m") to milliseconds
4
4
  */
5
5
  export declare function parseDuration(duration: string): number;
6
+ /**
7
+ * Parse a command-line timeout such as "10m", "90s" or "1h"; a bare number
8
+ * is minutes. The grammar is the one of `trafic --timeout`, which the CLI
9
+ * passes on as is. Returns undefined for anything else, or for zero.
10
+ */
11
+ export declare function parseTimeout(value: string): number | undefined;
6
12
  /**
7
13
  * Load configuration from TOML file
8
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAmC,MAAM,aAAa,CAAC;AA6BhF;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,CAkC3D;AAqFD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CA+B5D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAiD,MAAM,aAAa,CAAC;AAmC9F;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUtD;AAKD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ9D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,CAuC3D;AAkGD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAwD5D"}
@@ -1,5 +1,5 @@
1
1
  import { DatabaseSync } from "node:sqlite";
2
- import type { ProjectRecord, AccessLog } from "../types.js";
2
+ import type { ProjectRecord, AccessLog, ScheduledDestroy } from "../types.js";
3
3
  /**
4
4
  * Reads the current time in epoch milliseconds.
5
5
  *
@@ -31,12 +31,87 @@ export declare function updateProjectAccess(name: string, now?: Clock): void;
31
31
  export declare function getProject(name: string): ProjectRecord | undefined;
32
32
  /**
33
33
  * Set project status
34
+ *
35
+ * A busy status ("starting", "destroying") records the id of this process,
36
+ * so releaseStaleStatuses can tell when the process that set it is gone.
34
37
  */
35
38
  export declare function setProjectStatus(name: string, status: ProjectRecord["status"], now?: Clock): void;
39
+ /**
40
+ * Whether a process is alive. EPERM means it is, but belongs to another user.
41
+ */
42
+ export declare function isProcessAlive(pid: number): boolean;
43
+ /**
44
+ * Set back to "running" the busy statuses ("starting", "destroying") whose
45
+ * process is gone, and return the names of those projects.
46
+ *
47
+ * The process that sets a busy status sets it back when it ends. A process
48
+ * that is killed (a cancelled CI job, a dropped SSH connection, a restart of
49
+ * the service) cannot, and the status would stay: the sweeps never stop a
50
+ * busy project, max_running_projects counts it as running, and the waiting
51
+ * page never starts it. "running" lets the idle sweep check the project
52
+ * against DDEV and stop it, also when it is stopped already.
53
+ *
54
+ * `isAlive` says whether a process still works on its project: the service
55
+ * passes its own check at start, where its own id is not alive yet.
56
+ */
57
+ export declare function releaseStaleStatuses(isAlive?: (pid: number) => boolean): string[];
36
58
  /**
37
59
  * Get idle projects (last access older than threshold)
38
60
  */
39
61
  export declare function getIdleProjects(thresholdMs: number, now?: Clock): ProjectRecord[];
62
+ /**
63
+ * Delete every record of a project: its status row, its access logs and its
64
+ * scheduled removal. Returns whether there was any.
65
+ */
66
+ export declare function deleteProject(name: string): boolean;
67
+ /**
68
+ * Delete the records of the projects that DDEV no longer lists, and return
69
+ * their names.
70
+ *
71
+ * A project removed outside the agent (`ddev delete` by hand, or a
72
+ * `trafic destroy` older than the agent's own removal) leaves its row behind.
73
+ * releaseStaleStatuses would set that row back to "running", and every sweep
74
+ * would then ask DDEV about a project it does not know.
75
+ *
76
+ * A project with a busy status whose process lives is kept: `ddev delete`
77
+ * takes it out of the list before its removal deletes the records. A project
78
+ * with a scheduled removal is kept too: `ddev delete` by hand leaves its
79
+ * directory, and the scheduled removal still has to remove it. An empty
80
+ * list is kept as is too: a missing or unreadable project list is more likely
81
+ * than a server with no project, and keeping the rows is harmless.
82
+ */
83
+ export declare function deleteUnlistedProjects(listed: ReadonlySet<string>, isAlive?: (pid: number) => boolean): string[];
84
+ /**
85
+ * Schedule the removal of a project, or replace a removal already
86
+ * scheduled. `timeoutMs` is the time limit of the backup.
87
+ */
88
+ export declare function scheduleDestroy(name: string, destroyAt: number, backup: boolean, timeoutMs?: number): void;
89
+ /**
90
+ * Cancel the scheduled removal of a project:
91
+ * - "cancelled" when it was scheduled and is now cancelled
92
+ * - "none" when there was none
93
+ * - "removing" when the sweep already removes the project (see
94
+ * startScheduledDestroy): it is too late, and the removal stays
95
+ */
96
+ export declare function cancelScheduledDestroy(name: string): "cancelled" | "none" | "removing";
97
+ /**
98
+ * Mark a scheduled removal as started, when it is still due at `dueBy`.
99
+ * From then on, a cancel is refused, and a retry after a failure does not
100
+ * back the project up again. Returns false when the removal was cancelled
101
+ * or moved to a later date meanwhile. One statement, so a cancel from
102
+ * another process comes either before (false) or after (refused).
103
+ */
104
+ export declare function startScheduledDestroy(name: string, dueBy: number): boolean;
105
+ /**
106
+ * Get the scheduled removals, soonest first. With `dueBy`, only the removals
107
+ * due at or before that time.
108
+ */
109
+ export declare function getScheduledDestroys(dueBy?: number): ScheduledDestroy[];
110
+ /**
111
+ * Get the scheduled removal of a project when it is due at or before
112
+ * `dueBy`, or undefined when there is none.
113
+ */
114
+ export declare function getScheduledDestroy(name: string, dueBy?: number): ScheduledDestroy | undefined;
40
115
  /**
41
116
  * Log an access
42
117
  */
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/utils/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC;AAEjC,2DAA2D;AAC3D,eAAO,MAAM,WAAW,EAAE,KAAwB,CAAC;AAInD;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CA0BnD;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,YAAY,CAKpC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,KAAmB,GAAG,IAAI,CAQhF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAMlE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC/B,GAAG,GAAE,KAAmB,GACvB,IAAI,CAQN;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,GAAG,GAAE,KAAmB,GACvB,aAAa,EAAE,CASjB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,IAAI,CAO1D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,SAAS,EAAE,CAUvE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,KAAmB,GAAG,MAAM,CAM3E"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/utils/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC;AAEjC,2DAA2D;AAC3D,eAAO,MAAM,WAAW,EAAE,KAAwB,CAAC;AAanD;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAsCnD;AAiBD;;GAEG;AACH,wBAAgB,KAAK,IAAI,YAAY,CAKpC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,KAAmB,GAAG,IAAI,CAQhF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAMlE;AAQD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC/B,GAAG,GAAE,KAAmB,GACvB,IAAI,CAUN;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,GACjD,MAAM,EAAE,CA0BV;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,GAAG,GAAE,KAAmB,GACvB,aAAa,EAAE,CASjB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQnD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,EAC3B,OAAO,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,GACjD,MAAM,EAAE,CA0BV;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,IAAI,CAWN;AA+BD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,UAAU,CAWtF;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAM1E;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,SAA0B,GAAG,gBAAgB,EAAE,CAUxF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,KAAK,SAA0B,GAC9B,gBAAgB,GAAG,SAAS,CAS9B;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,IAAI,CAO1D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,SAAS,EAAE,CAUvE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,KAAmB,GAAG,MAAM,CAM3E"}
@@ -5,8 +5,8 @@ import type { DdevProject } from "../types.js";
5
5
  *
6
6
  * The file nests the path under the project name:
7
7
  *
8
- * scalian:
9
- * approot: /home/ddev/www/scalian
8
+ * my-app:
9
+ * approot: /home/ddev/www/my-app
10
10
  *
11
11
  * Indentation therefore has to be read. A flat key/value pass produces one
12
12
  * entry per line instead: the project mapped to an empty string, plus a
@@ -47,8 +47,11 @@ export interface DdevResult {
47
47
  /**
48
48
  * Runs a ddev command. Injected so tests drive the outcome directly instead
49
49
  * of replacing node:child_process for the whole module.
50
+ *
51
+ * `cwd` is for the commands that act on the project in that directory rather
52
+ * than on a project named in `args`.
50
53
  */
51
- export type DdevRunner = (args: string[], timeoutMs: number) => Promise<DdevResult>;
54
+ export type DdevRunner = (args: string[], timeoutMs: number, cwd?: string) => Promise<DdevResult>;
52
55
  /** The real runner, used unless a caller passes its own. */
53
56
  export declare const nodeDdevRunner: DdevRunner;
54
57
  /**
@@ -67,7 +70,11 @@ export declare function startProject(name: string, run?: DdevRunner): Promise<bo
67
70
  */
68
71
  export declare function stopProject(name: string, run?: DdevRunner): Promise<boolean>;
69
72
  /**
70
- * List all DDEV projects with their status
73
+ * List all DDEV projects with their live status.
74
+ *
75
+ * `ddev list -j` asks Docker, so a project started or stopped outside the
76
+ * agent (by `trafic deploy`, say) shows its real state here. It wraps the
77
+ * entries in a `raw` array.
71
78
  */
72
- export declare function listProjects(): DdevProject[];
79
+ export declare function listProjects(run?: DdevRunner): Promise<DdevProject[]>;
73
80
  //# sourceMappingURL=ddev.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ddev.d.ts","sourceRoot":"","sources":["../../src/utils/ddev.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoB/C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAC7B,eAAe,EAAE,MAAM,GACtB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA+BrB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,GAAG,EAAE,MAAM,GACV,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASrB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,MAAM,GAAG,SAAS,CAEpB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,IAAI,GACnB,IAAI,CAWN;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,CACvB,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,MAAM,KACd,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,4DAA4D;AAC5D,eAAO,MAAM,cAAc,EAAE,UAgBzB,CAAC;AAEL;;GAEG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,UAA2B,GAC/B,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CA0BlC;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,UAA2B,GAC/B,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,UAA2B,GAC/B,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,WAAW,EAAE,CAqB5C"}
1
+ {"version":3,"file":"ddev.d.ts","sourceRoot":"","sources":["../../src/utils/ddev.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoB/C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAC7B,eAAe,EAAE,MAAM,GACtB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA+BrB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,GAAG,EAAE,MAAM,GACV,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASrB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,MAAM,GAAG,SAAS,CAEpB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,IAAI,GACnB,IAAI,CAWN;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,CACvB,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,MAAM,KACT,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,4DAA4D;AAC5D,eAAO,MAAM,cAAc,EAAE,UAgBzB,CAAC;AAEL;;GAEG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,UAA2B,GAC/B,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CA2BlC;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,UAA2B,GAC/B,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,UAA2B,GAC/B,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,GAAG,GAAE,UAA2B,GAC/B,OAAO,CAAC,WAAW,EAAE,CAAC,CAuBxB"}
@@ -0,0 +1,3 @@
1
+ /** Whether `child` is strictly inside `parent`. Both are resolved paths. */
2
+ export declare function isInside(parent: string, child: string): boolean;
3
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG/D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studiometa/trafic-agent",
3
- "version": "0.1.49",
3
+ "version": "0.1.51",
4
4
  "description": "Server agent for DDEV preview environments — auth, scale-to-zero, auto-start",
5
5
  "keywords": [
6
6
  "agent",