@kici-dev/orchestrator 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/dispatcher.d.ts +27 -1
- package/dist/agent/host-roster-reaper.d.ts +42 -0
- package/dist/agent/host-roster.d.ts +110 -0
- package/dist/agent/registry.d.ts +65 -2
- package/dist/app.d.ts +3 -0
- package/dist/approvals/apply-decision.d.ts +11 -1
- package/dist/cache/pending-inits.d.ts +5 -0
- package/dist/cli/api-client.d.ts +11 -0
- package/dist/cli/commands/host.d.ts +13 -0
- package/dist/cli/commands/local-hook.d.ts +15 -0
- package/dist/cli/commands/local-trigger.d.ts +24 -0
- package/dist/cli/commands/remote-source.d.ts +17 -0
- package/dist/cli/commands/shared/versioned-upgrade.d.ts +37 -0
- package/dist/cli/kici-admin.d.ts +13 -0
- package/dist/cli/service/compose.d.ts +2 -1
- package/dist/cli/service/index.d.ts +2 -2
- package/dist/cli/service/instance/manifest.d.ts +12 -0
- package/dist/cli/service/instance/resolve.d.ts +12 -2
- package/dist/cli/service/launchd.d.ts +4 -1
- package/dist/cli/service/platform-detect.d.ts +5 -0
- package/dist/cli/service/systemd.d.ts +2 -1
- package/dist/cli/service/types.d.ts +37 -0
- package/dist/cli/service/windows.d.ts +2 -1
- package/dist/cli.js +1340 -198
- package/dist/cluster/coordinator.d.ts +5 -1
- package/dist/config/schema.d.ts +4 -0
- package/dist/config/types.d.ts +9 -0
- package/dist/config.d.ts +6 -4
- package/dist/dashboard/handler.d.ts +26 -2
- package/dist/dashboard/needs-edges.d.ts +13 -0
- package/dist/db/migrations/035_pending_workflow_contexts.d.ts +11 -0
- package/dist/db/migrations/036_attestations.d.ts +15 -0
- package/dist/db/migrations/037_generic_sources_provider_type_local.d.ts +19 -0
- package/dist/db/migrations/038_remote_sources.d.ts +14 -0
- package/dist/db/migrations/039_host_roster.d.ts +19 -0
- package/dist/db/migrations/040_runsonall_pin.d.ts +4 -0
- package/dist/db/migrations/041_wave_gated.d.ts +4 -0
- package/dist/db/migrations/042_dispatch_queue_patterns.d.ts +4 -0
- package/dist/db/types.d.ts +144 -10
- package/dist/diagnostics/fleet-collector.d.ts +1 -1
- package/dist/entry-helpers.d.ts +7 -29
- package/dist/environments/held-runs.d.ts +33 -0
- package/dist/index.js +1 -0
- package/dist/lockfile-redos-guard.d.ts +19 -0
- package/dist/metrics/prometheus.d.ts +17 -2
- package/dist/metrics/scheduled-jobs.d.ts +2 -2
- package/dist/orchestrator-core.d.ts +94 -1
- package/dist/pipeline/dispatch-matched-workflow.d.ts +60 -2
- package/dist/pipeline/install-secrets-resolver.d.ts +32 -5
- package/dist/pipeline/needs-scheduler.d.ts +12 -10
- package/dist/pipeline/pending-workflow-context.d.ts +44 -0
- package/dist/pipeline/processor.d.ts +31 -4
- package/dist/pipeline/remote-source-store.d.ts +21 -0
- package/dist/pipeline/resume-workflow.d.ts +26 -0
- package/dist/pipeline/wave-scheduler.d.ts +60 -0
- package/dist/providers/local/index.d.ts +33 -0
- package/dist/providers/local/local-source-config.d.ts +17 -0
- package/dist/providers/{internal → local}/lock-file-fetcher.d.ts +6 -6
- package/dist/providers/{internal → local}/normalizer.d.ts +28 -28
- package/dist/providers/{internal → local}/repo-url-builder.d.ts +6 -6
- package/dist/queue/job-queue.d.ts +61 -2
- package/dist/reporting/execution-tracker.d.ts +61 -0
- package/dist/routes/admin-events.d.ts +3 -4
- package/dist/routes/uploads.d.ts +36 -26
- package/dist/server.js +53755 -50888
- package/dist/sources/build-platform-sources.d.ts +4 -2
- package/dist/stale-detector/stale-run-detector.d.ts +14 -1
- package/dist/standalone.js +17234 -15725
- package/dist/webhook/generic-sources-listener.d.ts +4 -0
- package/dist/webhook/generic-sources.d.ts +37 -11
- package/dist/webhook/register-source-bundle.d.ts +14 -4
- package/dist/ws/agent-handler.d.ts +17 -0
- package/dist/ws/dashboard-dispatch-guard.d.ts +21 -0
- package/dist/ws/dashboard-env-handler.d.ts +13 -1
- package/dist/ws/oidc-token-relay.d.ts +59 -0
- package/dist/ws/platform-client.d.ts +30 -1
- package/dist/ws/test-relay-handlers.d.ts +112 -0
- package/installer-image-digests.json +3 -3
- package/package.json +22 -22
- package/sbom.spdx.json +3228 -6633
- package/dist/providers/internal/index.d.ts +0 -32
- package/dist/routes/test-trigger.d.ts +0 -41
- package/dist/ws/observer-handler.d.ts +0 -42
|
@@ -55,6 +55,13 @@ export declare class Dispatcher {
|
|
|
55
55
|
private readonly agentJobs;
|
|
56
56
|
/** Reverse map: jobId -> agentId, for cancel-run lookups. */
|
|
57
57
|
private readonly jobToAgent;
|
|
58
|
+
/**
|
|
59
|
+
* jobId -> runId for currently-tracked jobs. Populated alongside
|
|
60
|
+
* `jobToAgent`/`agentJobs` at dispatch time and cleared with them, so the
|
|
61
|
+
* provenance token relay can resolve a job's runId from the dispatcher's own
|
|
62
|
+
* state (`resolveOwnedJob`) rather than trusting an agent-asserted value.
|
|
63
|
+
*/
|
|
64
|
+
private readonly jobRunIds;
|
|
58
65
|
/**
|
|
59
66
|
* Grace window for recently completed jobs.
|
|
60
67
|
* Allows late messages (log.chunk, step.status) to be accepted for a short
|
|
@@ -143,6 +150,15 @@ export declare class Dispatcher {
|
|
|
143
150
|
* 4. If queue full: return 'rejected'.
|
|
144
151
|
*/
|
|
145
152
|
dispatch(job: QueuedJobInput): Promise<DispatchResult>;
|
|
153
|
+
/**
|
|
154
|
+
* Dispatch a host-fanout pinned child. The job targets exactly
|
|
155
|
+
* `job.pinnedAgentId`: if that agent is locally connected, satisfies the
|
|
156
|
+
* runsOn/exclude/mandatory gate, and has capacity, dispatch immediately;
|
|
157
|
+
* otherwise queue it WITH the pin so the pin-aware drain delivers it when the
|
|
158
|
+
* agent frees up or (re)connects. A pinned job never falls through to a
|
|
159
|
+
* different agent.
|
|
160
|
+
*/
|
|
161
|
+
private dispatchPinned;
|
|
146
162
|
/**
|
|
147
163
|
* Eagerly dispatch a specific bound job to a freshly-registered
|
|
148
164
|
* scaler-managed agent.
|
|
@@ -296,7 +312,7 @@ export declare class Dispatcher {
|
|
|
296
312
|
* reconnecting agent reporting the job in-flight means it had started,
|
|
297
313
|
* so mark it started for disconnect triage.
|
|
298
314
|
*/
|
|
299
|
-
restoreJobForAgent(agentId: string, jobId: string): void;
|
|
315
|
+
restoreJobForAgent(agentId: string, jobId: string, runId?: string): void;
|
|
300
316
|
/**
|
|
301
317
|
* Start a recovery timer for a job found in 'dispatched' state on startup.
|
|
302
318
|
* Called by server.ts/standalone.ts to create recovery timers for jobs
|
|
@@ -349,6 +365,16 @@ export declare class Dispatcher {
|
|
|
349
365
|
/** Remove expired entries from the completedJobs grace window map. */
|
|
350
366
|
private cleanupExpiredGraceEntries;
|
|
351
367
|
private trackJobForAgent;
|
|
368
|
+
/**
|
|
369
|
+
* Verify `agentId` currently owns `jobId`; if so, return the job's runId from
|
|
370
|
+
* the dispatcher's own tracking. Used by the provenance token relay so the
|
|
371
|
+
* orchestrator binds a minted token to a job the agent actually owns — the
|
|
372
|
+
* agent never asserts a runId. Returns `undefined` when the agent does not
|
|
373
|
+
* own the job (or the runId is no longer tracked).
|
|
374
|
+
*/
|
|
375
|
+
resolveOwnedJob(agentId: string, jobId: string): {
|
|
376
|
+
runId: string;
|
|
377
|
+
} | undefined;
|
|
352
378
|
/**
|
|
353
379
|
* Cancel a queued job (mark as failed in the dispatch queue).
|
|
354
380
|
* Used by the coordinator to clean up local fallback entries when a peer
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { HostRosterStore } from './host-roster.js';
|
|
2
|
+
export interface HostRosterReaperOptions {
|
|
3
|
+
store: Pick<HostRosterStore, 'reapEphemeralPastTtl' | 'countStaticUnreachable'>;
|
|
4
|
+
ttlMs: number;
|
|
5
|
+
/** Grace window for the connected-but-stale case in `countStaticUnreachable`. */
|
|
6
|
+
graceMs: number;
|
|
7
|
+
scanIntervalMs: number;
|
|
8
|
+
/** Setter for the `kici_orch_declared_hosts_unreachable` gauge. */
|
|
9
|
+
setUnreachableGauge: (value: number) => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Leader-only host-roster reaper.
|
|
13
|
+
*
|
|
14
|
+
* Modeled on `EventRetryScanner`: started/stopped via the Raft leadership
|
|
15
|
+
* callbacks, a single timer cleared on `stop()`. Each leader tick it:
|
|
16
|
+
*
|
|
17
|
+
* - DELETEs `ephemeral` host_roster rows whose `last_seen` is older than the ttl
|
|
18
|
+
* (scaled-down autoscale agents — silent GC, no alarm); and
|
|
19
|
+
* - counts `static` (declared) hosts whose derived status is `unreachable` and
|
|
20
|
+
* publishes that count to the `kici_orch_declared_hosts_unreachable` gauge, so
|
|
21
|
+
* a Mimir ruler alert can page an operator when a declared box goes dark.
|
|
22
|
+
*
|
|
23
|
+
* `static` unreachability is read-derived by `deriveHostStatus` (no write here),
|
|
24
|
+
* so the reaper never deletes a static row. Followers never run the reaper, so
|
|
25
|
+
* the gauge series exists only on the leader (the alert uses `max by ()`).
|
|
26
|
+
*/
|
|
27
|
+
export declare class HostRosterReaper {
|
|
28
|
+
private readonly store;
|
|
29
|
+
private readonly ttlMs;
|
|
30
|
+
private readonly graceMs;
|
|
31
|
+
private readonly scanIntervalMs;
|
|
32
|
+
private readonly setUnreachableGauge;
|
|
33
|
+
private timer;
|
|
34
|
+
private isLeader;
|
|
35
|
+
constructor(opts: HostRosterReaperOptions);
|
|
36
|
+
onBecomeLeader(): void;
|
|
37
|
+
onLoseLeadership(): void;
|
|
38
|
+
stop(): void;
|
|
39
|
+
/** One reap pass. Public for tests. */
|
|
40
|
+
tick(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=host-roster-reaper.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
import { type LabelMatcher } from '@kici-dev/engine';
|
|
3
|
+
import type { Database, HostRosterRow } from '../db/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Derived (read-time) status of a roster host. Never a stored mutable column:
|
|
6
|
+
* status is computed from the shared `last_seen` + `connected_instance_id` so
|
|
7
|
+
* every cluster instance agrees regardless of which one holds the live WS.
|
|
8
|
+
*/
|
|
9
|
+
export declare enum HostStatus {
|
|
10
|
+
/** Connected to some instance AND heartbeat fresh. */
|
|
11
|
+
ready = "ready",
|
|
12
|
+
/** Static host that is not currently live (declared, rebooting, or gone). */
|
|
13
|
+
unreachable = "unreachable",
|
|
14
|
+
/** Ephemeral host past ttl but not yet reaped (scaled down). */
|
|
15
|
+
stale = "stale"
|
|
16
|
+
}
|
|
17
|
+
/** The lifecycle class snapshot from the auth token's `agent_type`. */
|
|
18
|
+
export type LifecycleClass = 'static' | 'ephemeral';
|
|
19
|
+
/**
|
|
20
|
+
* A roster host that matched a `runsOnAll` predicate, with its derived status.
|
|
21
|
+
* The host-fanout resolver consumes this: `ready` hosts become pinned children;
|
|
22
|
+
* `unreachable` static hosts are subject to the `onUnreachable` policy; `stale`
|
|
23
|
+
* ephemeral hosts are skipped.
|
|
24
|
+
*/
|
|
25
|
+
export interface MatchedHost {
|
|
26
|
+
agentId: string;
|
|
27
|
+
/** Hostname (falls back to agentId). */
|
|
28
|
+
host: string;
|
|
29
|
+
labels: string[];
|
|
30
|
+
lifecycleClass: LifecycleClass;
|
|
31
|
+
/** Which orchestrator holds the live WS (null = disconnected). For the cross-cluster pin. */
|
|
32
|
+
connectedInstanceId: string | null;
|
|
33
|
+
status: HostStatus;
|
|
34
|
+
platform: string | null;
|
|
35
|
+
arch: string | null;
|
|
36
|
+
}
|
|
37
|
+
export interface UpsertHostInput {
|
|
38
|
+
agentId: string;
|
|
39
|
+
tokenId: string | null;
|
|
40
|
+
lifecycleClass: LifecycleClass;
|
|
41
|
+
labels: string[];
|
|
42
|
+
hostname: string | null;
|
|
43
|
+
platform: string;
|
|
44
|
+
arch: string;
|
|
45
|
+
instanceId: string;
|
|
46
|
+
}
|
|
47
|
+
/** Minimal row shape `deriveHostStatus` reads (the store row or the CLI row). */
|
|
48
|
+
export interface HostStatusRow {
|
|
49
|
+
connected_instance_id: string | null;
|
|
50
|
+
lifecycle_class: string;
|
|
51
|
+
last_seen: Date | string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The ONE status-derivation function — used by the store and the kici-admin
|
|
55
|
+
* host CLI. `ready` requires the host to be genuinely live (connected to some
|
|
56
|
+
* instance AND heartbeat fresh — the freshness check catches a crashed
|
|
57
|
+
* instance that never cleared `connected_instance_id`). It never returns
|
|
58
|
+
* `ready` for a not-currently-live host: a not-live `static` reads
|
|
59
|
+
* `unreachable` (the declared-but-absent alarm), a not-live `ephemeral` reads
|
|
60
|
+
* `stale` (scaled down, awaiting reap).
|
|
61
|
+
*/
|
|
62
|
+
export declare function deriveHostStatus(row: HostStatusRow, nowMs: number, graceMs: number): HostStatus;
|
|
63
|
+
/**
|
|
64
|
+
* Durable, cluster-shared roster of every agent the cluster has ever enrolled.
|
|
65
|
+
*
|
|
66
|
+
* The in-memory `AgentRegistry` reconciles into this table on register
|
|
67
|
+
* (`upsert`) / unregister (`markDisconnected`), with a coarse heartbeat
|
|
68
|
+
* (`stampLastSeen`). All liveness writes are owner-guarded by `instanceId` so a
|
|
69
|
+
* stale disconnect from an old instance can never clobber a row a different
|
|
70
|
+
* instance now owns. The leader-only reaper deletes `ephemeral` rows past their
|
|
71
|
+
* ttl via `reapEphemeralPastTtl`; `static` rows persist and read `unreachable`.
|
|
72
|
+
*/
|
|
73
|
+
export declare class HostRosterStore {
|
|
74
|
+
private readonly db;
|
|
75
|
+
constructor(db: Kysely<Database>);
|
|
76
|
+
/** Idempotent upsert on agent_id; stamps connected_instance_id + last_seen. */
|
|
77
|
+
upsert(input: UpsertHostInput): Promise<void>;
|
|
78
|
+
/** Clear liveness on disconnect — but only if THIS instance still owns it. */
|
|
79
|
+
markDisconnected(agentId: string, instanceId: string): Promise<void>;
|
|
80
|
+
/** Coarse heartbeat stamp — same owner-guard as markDisconnected. */
|
|
81
|
+
stampLastSeen(agentId: string, instanceId: string): Promise<void>;
|
|
82
|
+
/** Operator pre-declare of a static host before its agent dials in. */
|
|
83
|
+
declareStatic(input: {
|
|
84
|
+
agentId: string;
|
|
85
|
+
labels: string[];
|
|
86
|
+
hostname?: string;
|
|
87
|
+
}): Promise<void>;
|
|
88
|
+
get(agentId: string): Promise<HostRosterRow | null>;
|
|
89
|
+
listAll(): Promise<HostRosterRow[]>;
|
|
90
|
+
/**
|
|
91
|
+
* Resolve every roster host matching a `runsOnAll` predicate (OR-of-AND
|
|
92
|
+
* include groups, minus exclude labels), tagged with its derived status. This
|
|
93
|
+
* is the host-fanout resolver: it returns declared-but-absent static hosts
|
|
94
|
+
* (status `unreachable`) so the caller can apply `onUnreachable` — the live
|
|
95
|
+
* registry alone cannot name an expected-but-absent host.
|
|
96
|
+
*/
|
|
97
|
+
findMatching(include: readonly (readonly LabelMatcher[])[], exclude: readonly LabelMatcher[], graceMs: number): Promise<MatchedHost[]>;
|
|
98
|
+
/**
|
|
99
|
+
* Count `static` (declared) hosts whose derived status is `unreachable` —
|
|
100
|
+
* the "declared-but-absent" alarm population. Reuses the single-source
|
|
101
|
+
* {@link deriveHostStatus} so the count never diverges from what
|
|
102
|
+
* `kici-admin host list` shows. A not-currently-connected static host reads
|
|
103
|
+
* `unreachable` regardless of grace (only the connected-but-stale case
|
|
104
|
+
* depends on `graceMs`).
|
|
105
|
+
*/
|
|
106
|
+
countStaticUnreachable(graceMs: number): Promise<number>;
|
|
107
|
+
/** Delete ephemeral rows whose last_seen is older than ttl. Returns count. */
|
|
108
|
+
reapEphemeralPastTtl(ttlMs: number): Promise<number>;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=host-roster.d.ts.map
|
package/dist/agent/registry.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* No persistence needed -- agents re-register on reconnect.
|
|
10
10
|
*/
|
|
11
|
+
import { type LabelMatcher } from '@kici-dev/engine';
|
|
11
12
|
import type { WsLike } from '@kici-dev/engine';
|
|
12
13
|
export type { WsLike } from '@kici-dev/engine';
|
|
13
14
|
/**
|
|
@@ -76,6 +77,13 @@ export interface AgentEntry {
|
|
|
76
77
|
memoryUsedMb: number | null;
|
|
77
78
|
memoryAvailableMb: number | null;
|
|
78
79
|
uptimeSeconds: number | null;
|
|
80
|
+
/**
|
|
81
|
+
* The auth token's `agent_type` (`'static'` | `'ephemeral'`) snapshot, or
|
|
82
|
+
* `null` when auth mode is `none`. Drives the host roster's
|
|
83
|
+
* `lifecycle_class` — `static` rows persist + alarm on absence, `ephemeral`
|
|
84
|
+
* rows are GC'd past their ttl.
|
|
85
|
+
*/
|
|
86
|
+
tokenAgentType: 'static' | 'ephemeral' | null;
|
|
79
87
|
}
|
|
80
88
|
/** Optional static metadata from the agent.register message. */
|
|
81
89
|
interface AgentMetadata {
|
|
@@ -106,6 +114,36 @@ interface AgentMetadata {
|
|
|
106
114
|
* scaler registration metadata.
|
|
107
115
|
*/
|
|
108
116
|
scalerManaged?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* The auth token's `agent_type` (`'static'` | `'ephemeral'`), threaded from
|
|
119
|
+
* `agent-handler.ts` so the host roster can snapshot it as the row's
|
|
120
|
+
* `lifecycle_class`. `null` / undefined when auth mode is `none`.
|
|
121
|
+
*/
|
|
122
|
+
tokenAgentType?: 'static' | 'ephemeral' | null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Subset of `HostRosterStore` the registry reconciles into. Optional — when no
|
|
126
|
+
* store is injected (e.g. workers, which have no DB), the reconcile hooks are
|
|
127
|
+
* a no-op and the in-memory registry behaves exactly as before.
|
|
128
|
+
*/
|
|
129
|
+
export interface RosterReconciler {
|
|
130
|
+
upsert(input: {
|
|
131
|
+
agentId: string;
|
|
132
|
+
tokenId: string | null;
|
|
133
|
+
lifecycleClass: 'static' | 'ephemeral';
|
|
134
|
+
labels: string[];
|
|
135
|
+
hostname: string | null;
|
|
136
|
+
platform: string;
|
|
137
|
+
arch: string;
|
|
138
|
+
instanceId: string;
|
|
139
|
+
}): Promise<void>;
|
|
140
|
+
markDisconnected(agentId: string, instanceId: string): Promise<void>;
|
|
141
|
+
stampLastSeen(agentId: string, instanceId: string): Promise<void>;
|
|
142
|
+
}
|
|
143
|
+
/** Optional dependencies that enable host-roster reconciliation. */
|
|
144
|
+
export interface AgentRegistryDeps {
|
|
145
|
+
rosterStore?: RosterReconciler;
|
|
146
|
+
instanceId?: string;
|
|
109
147
|
}
|
|
110
148
|
export declare class AgentRegistry {
|
|
111
149
|
/** Primary: agentId -> AgentEntry */
|
|
@@ -143,6 +181,24 @@ export declare class AgentRegistry {
|
|
|
143
181
|
* propagation path to in-flight WS connections.
|
|
144
182
|
*/
|
|
145
183
|
private readonly tokenExpiryTimers;
|
|
184
|
+
/**
|
|
185
|
+
* Per-agent last roster `last_seen` stamp time (epoch ms). Throttles the
|
|
186
|
+
* coarse heartbeat write so we don't issue a DB write per agent per
|
|
187
|
+
* heartbeat — `grace`/`ttl` are minute-scale, so a throttled stamp is
|
|
188
|
+
* sufficient. Cleaned up alongside the agent in `removeFromIndexes`.
|
|
189
|
+
*/
|
|
190
|
+
private readonly lastRosterStampAt;
|
|
191
|
+
/** Minimum interval between coarse roster `last_seen` stamps per agent. */
|
|
192
|
+
private readonly ROSTER_STAMP_THROTTLE_MS;
|
|
193
|
+
/** Optional host-roster reconciler + this instance's id (see AgentRegistryDeps). */
|
|
194
|
+
private readonly rosterStore?;
|
|
195
|
+
private readonly instanceId?;
|
|
196
|
+
/**
|
|
197
|
+
* The roster reconcile seam is optional: when no store is injected (workers
|
|
198
|
+
* with no DB, unit tests), every reconcile hook is a no-op and the in-memory
|
|
199
|
+
* registry behaves exactly as before.
|
|
200
|
+
*/
|
|
201
|
+
constructor(deps?: AgentRegistryDeps);
|
|
146
202
|
/**
|
|
147
203
|
* Register an agent. If the agentId already exists, updates the existing
|
|
148
204
|
* entry (agent reconnection scenario).
|
|
@@ -209,7 +265,14 @@ export declare class AgentRegistry {
|
|
|
209
265
|
* @param requiredLabels - All labels the agent must have (intersection semantics).
|
|
210
266
|
* @returns Array of matching available AgentEntry objects.
|
|
211
267
|
*/
|
|
212
|
-
findAvailable(requiredLabels: string[], excludeLabels?: string[]): AgentEntry[];
|
|
268
|
+
findAvailable(requiredLabels: string[], requiredPatterns?: LabelMatcher[], excludeLabels?: string[], excludePatterns?: LabelMatcher[]): AgentEntry[];
|
|
269
|
+
/**
|
|
270
|
+
* Whether a specific agent satisfies the same label / exclude / mandatory
|
|
271
|
+
* gate `findAvailable` applies per-agent, ignoring capacity. Used by the
|
|
272
|
+
* host-fanout pin to verify the resolved agent still matches before pinning
|
|
273
|
+
* (guards against roster label drift between resolution and dispatch).
|
|
274
|
+
*/
|
|
275
|
+
agentSatisfies(agent: AgentEntry, requiredLabels: string[], requiredPatterns?: LabelMatcher[], excludeLabels?: string[], excludePatterns?: LabelMatcher[]): boolean;
|
|
213
276
|
/**
|
|
214
277
|
* Check if ANY registered agent matches the required labels, regardless of capacity.
|
|
215
278
|
* Used to decide whether to queue (agent exists but busy) vs reject (no agent at all).
|
|
@@ -221,7 +284,7 @@ export declare class AgentRegistry {
|
|
|
221
284
|
* an off-gate gated agent as "matching but busy" and skip the peer
|
|
222
285
|
* reroute path even though the local agent can never accept the job.
|
|
223
286
|
*/
|
|
224
|
-
hasMatchingAgent(requiredLabels: string[], excludeLabels?: string[]): boolean;
|
|
287
|
+
hasMatchingAgent(requiredLabels: string[], requiredPatterns?: LabelMatcher[], excludeLabels?: string[], excludePatterns?: LabelMatcher[]): boolean;
|
|
225
288
|
/**
|
|
226
289
|
* Internal helper: intersect the per-label candidate sets to find agentIds
|
|
227
290
|
* that satisfy `requiredLabels`. Returns `null` when any required label
|
package/dist/app.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ import type { Kysely } from 'kysely';
|
|
|
16
16
|
import type pg from 'pg';
|
|
17
17
|
import { type ColdStore } from '@kici-dev/shared';
|
|
18
18
|
import type { AppConfig } from './config.js';
|
|
19
|
+
import type { HostRosterStore } from './agent/host-roster.js';
|
|
19
20
|
import type { Database } from './db/types.js';
|
|
20
21
|
import type { AgentRegistry } from './agent/registry.js';
|
|
21
22
|
import type { Dispatcher } from './agent/dispatcher.js';
|
|
@@ -81,6 +82,8 @@ export interface AppDependencies {
|
|
|
81
82
|
db: Kysely<Database>;
|
|
82
83
|
pool: pg.Pool;
|
|
83
84
|
registry: AgentRegistry;
|
|
85
|
+
/** Host roster store for runsOnAll fan-out resolution. */
|
|
86
|
+
hostRosterStore?: HostRosterStore;
|
|
84
87
|
dispatcher: Dispatcher;
|
|
85
88
|
jobQueue: JobQueue;
|
|
86
89
|
dedup: DedupCache;
|
|
@@ -40,15 +40,25 @@ export interface ApplyDecisionDeps {
|
|
|
40
40
|
allowSelfApproval: boolean;
|
|
41
41
|
/** Resolve the Keycloak sub of the user who triggered a run (for the self-approval gate). */
|
|
42
42
|
resolveTriggererSub: (runId: string) => Promise<string | undefined>;
|
|
43
|
-
/** Called when a job
|
|
43
|
+
/** Called when a job hold is released — re-dispatch the element. */
|
|
44
44
|
onJobRelease: (signal: ReleaseSignal) => Promise<void>;
|
|
45
45
|
/** Called when a step hold is released — notify the waiting agent (approved). */
|
|
46
46
|
onStepRelease?: (signal: ReleaseSignal) => Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Called when a workflow-scoped hold is released (install gate approved) —
|
|
49
|
+
* rebuild the dispatch context and resume the workflow from the install gate.
|
|
50
|
+
*/
|
|
51
|
+
onWorkflowRelease?: (signal: ReleaseSignal) => Promise<void>;
|
|
47
52
|
/**
|
|
48
53
|
* Called when a step-scoped hold is rejected — notify the waiting agent so it
|
|
49
54
|
* fails the step instead of blocking until expiry. Carries the holdId.
|
|
50
55
|
*/
|
|
51
56
|
onStepReject?: (heldRunId: string, reason?: string) => Promise<void> | void;
|
|
57
|
+
/**
|
|
58
|
+
* Called when a workflow-scoped hold is rejected (install gate rejected) —
|
|
59
|
+
* cancel the run and drop the pending workflow context. Carries the runId.
|
|
60
|
+
*/
|
|
61
|
+
onWorkflowReject?: (runId: string) => Promise<void>;
|
|
52
62
|
}
|
|
53
63
|
/** Apply a single approve/reject decision to a hold. */
|
|
54
64
|
export declare function applyDecision(deps: ApplyDecisionDeps, args: {
|
|
@@ -12,6 +12,11 @@ export interface InitResult {
|
|
|
12
12
|
environmentName?: string;
|
|
13
13
|
env?: Record<string, string>;
|
|
14
14
|
concurrencyGroup?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Resolved matrix combinations when the target job's matrix is a dynamic
|
|
17
|
+
* function. The dispatch path re-materializes these into N execution jobs.
|
|
18
|
+
*/
|
|
19
|
+
matrixValues?: Array<Record<string, string | undefined>>;
|
|
15
20
|
}
|
|
16
21
|
export declare class PendingInitTracker extends PendingTracker<InitResult> {
|
|
17
22
|
constructor();
|
package/dist/cli/api-client.d.ts
CHANGED
|
@@ -25,6 +25,14 @@ export interface FleetTopologyResponse {
|
|
|
25
25
|
* avoid a cross-package import in the CLI wire type.
|
|
26
26
|
*/
|
|
27
27
|
export type GenericSourceGitConfigPayload = Record<string, unknown>;
|
|
28
|
+
/**
|
|
29
|
+
* Wire shape for a local filesystem source's config (`{ repoBasePath, cloneUrlBase? }`).
|
|
30
|
+
* Matches `LocalSourceConfigSchema` in `providers/local/local-source-config.ts`.
|
|
31
|
+
*/
|
|
32
|
+
export interface GenericSourceLocalConfigPayload {
|
|
33
|
+
repoBasePath: string;
|
|
34
|
+
cloneUrlBase?: string;
|
|
35
|
+
}
|
|
28
36
|
/**
|
|
29
37
|
* Response shape for generic webhook sources from the admin API.
|
|
30
38
|
*/
|
|
@@ -134,6 +142,7 @@ export declare class AdminApiClient {
|
|
|
134
142
|
rateLimitRpm?: number;
|
|
135
143
|
providerType?: string;
|
|
136
144
|
gitConfig?: GenericSourceGitConfigPayload;
|
|
145
|
+
localConfig?: GenericSourceLocalConfigPayload;
|
|
137
146
|
}): Promise<{
|
|
138
147
|
source: GenericSourceResponse;
|
|
139
148
|
}>;
|
|
@@ -159,6 +168,8 @@ export declare class AdminApiClient {
|
|
|
159
168
|
providerType?: string;
|
|
160
169
|
/** `null` clears the config; omit to leave unchanged. */
|
|
161
170
|
gitConfig?: GenericSourceGitConfigPayload | null;
|
|
171
|
+
/** `null` clears the config; omit to leave unchanged. */
|
|
172
|
+
localConfig?: GenericSourceLocalConfigPayload | null;
|
|
162
173
|
}): Promise<{
|
|
163
174
|
source: GenericSourceResponse;
|
|
164
175
|
}>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host roster management commands for kici-admin (orchestrator data plane).
|
|
3
|
+
*
|
|
4
|
+
* host list List all roster hosts with derived status
|
|
5
|
+
* host get Show one roster host
|
|
6
|
+
* host declare Pre-declare a static host before its agent connects
|
|
7
|
+
*
|
|
8
|
+
* Reads/writes the orchestrator DB directly (via withDb), so this lives in
|
|
9
|
+
* kici-admin — never in the public `kici` or the Platform `kici-platform-admin`.
|
|
10
|
+
*/
|
|
11
|
+
import type { Command } from 'commander';
|
|
12
|
+
export declare function registerHostCommands(program: Command): void;
|
|
13
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the post-receive hook script. For each pushed ref the hook calls
|
|
3
|
+
* `kici-admin source trigger-local <id> --ref <ref> --sha <new> --base-url <url>`,
|
|
4
|
+
* so a push to any branch dispatches a run for that ref.
|
|
5
|
+
*/
|
|
6
|
+
export declare function renderPostReceiveHook(opts: {
|
|
7
|
+
sourceId: string;
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
}): string;
|
|
10
|
+
/**
|
|
11
|
+
* Write the rendered hook into the repo's `.git/hooks/post-receive` and make it
|
|
12
|
+
* executable. Throws when the path is not a git repo (no `.git/hooks` dir).
|
|
13
|
+
*/
|
|
14
|
+
export declare function installPostReceiveHook(repoPath: string, script: string): string;
|
|
15
|
+
//# sourceMappingURL=local-hook.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface LocalTriggerInput {
|
|
2
|
+
orgId: string;
|
|
3
|
+
sourceId: string;
|
|
4
|
+
repoFullName: string;
|
|
5
|
+
event: 'push' | 'pull_request';
|
|
6
|
+
ref: string;
|
|
7
|
+
sha: string;
|
|
8
|
+
defaultBranch: string;
|
|
9
|
+
}
|
|
10
|
+
export interface LocalTriggerRequest {
|
|
11
|
+
path: string;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
body: string;
|
|
14
|
+
}
|
|
15
|
+
/** Build the GitHub-shaped webhook request the local provider normalizer expects. */
|
|
16
|
+
export declare function buildLocalTriggerRequest(input: LocalTriggerInput): LocalTriggerRequest;
|
|
17
|
+
/** Read HEAD ref + sha from a local git repo (used when the operator omits --ref/--sha). */
|
|
18
|
+
export declare function readRepoHead(repoPath: string): {
|
|
19
|
+
ref: string;
|
|
20
|
+
sha: string;
|
|
21
|
+
};
|
|
22
|
+
/** POST the trigger request to the orchestrator base URL. Returns the HTTP status. */
|
|
23
|
+
export declare function sendLocalTrigger(baseUrl: string, req: LocalTriggerRequest): Promise<number>;
|
|
24
|
+
//# sourceMappingURL=local-trigger.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote-source inspection command for kici-admin.
|
|
3
|
+
*
|
|
4
|
+
* Subcommand namespace: `kici-admin remote-source show`.
|
|
5
|
+
*
|
|
6
|
+
* The remote-source anchor (`remote_sources` table) maps the deterministic
|
|
7
|
+
* routing key `remote:<orgId>` to the orchestrator's canonical org id so a
|
|
8
|
+
* Platform-relayed `kici run remote` resolves the real tenant. It is
|
|
9
|
+
* auto-provisioned on Platform auth; this command lets an operator inspect or
|
|
10
|
+
* confirm the row when debugging org-anchor issues on a hidden orchestrator.
|
|
11
|
+
*
|
|
12
|
+
* Reads the orchestrator DB directly (on-host operator inspection), so it stays
|
|
13
|
+
* usable even when Platform is unreachable.
|
|
14
|
+
*/
|
|
15
|
+
import type { Command } from 'commander';
|
|
16
|
+
export declare function registerRemoteSourceCommands(program: Command): void;
|
|
17
|
+
//# sourceMappingURL=remote-source.d.ts.map
|
|
@@ -85,6 +85,43 @@ export declare function getInstallBase(platform: ServicePlatform, name: string):
|
|
|
85
85
|
* 5. Update symlink (Unix) or service registration (Windows)
|
|
86
86
|
* 6. Start service
|
|
87
87
|
*/
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the target version for an npm-source upgrade (no --from/--url).
|
|
90
|
+
*
|
|
91
|
+
* The npm-source flow assumes the operator already ran
|
|
92
|
+
* `npm install -g @kici-dev/<pkg>@<version>`, which overwrites the global
|
|
93
|
+
* package in place. The running `kici-admin` binary is therefore the new
|
|
94
|
+
* version, and `running` is its self-reported version. We default the target
|
|
95
|
+
* to it, or validate an explicitly-passed --version matches — a mismatch means
|
|
96
|
+
* the npm install did not actually update the global binary.
|
|
97
|
+
*/
|
|
98
|
+
export declare function resolveNpmSourceVersion(opts: {
|
|
99
|
+
requested: string | undefined;
|
|
100
|
+
running: string;
|
|
101
|
+
}): string;
|
|
102
|
+
/** Verdict from {@link verifyNpmSourceLaunch}. */
|
|
103
|
+
export type NpmSourceLaunchVerdict = {
|
|
104
|
+
ok: true;
|
|
105
|
+
version: string;
|
|
106
|
+
manifestVersion: string | null;
|
|
107
|
+
} | {
|
|
108
|
+
ok: false;
|
|
109
|
+
reason: string;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Decide whether an npm-source upgrade may proceed, given the version the
|
|
113
|
+
* invoking CLI is (`invoked`) and the version the installed unit will actually
|
|
114
|
+
* launch (`launched`, or null when unresolvable — e.g. an opaque --binary
|
|
115
|
+
* install). On success `manifestVersion` is the value to persist (null = leave
|
|
116
|
+
* the manifest's kiciVersion unchanged because we couldn't verify it).
|
|
117
|
+
*/
|
|
118
|
+
export declare function verifyNpmSourceLaunch(opts: {
|
|
119
|
+
component: UpgradeComponent;
|
|
120
|
+
invoked: string;
|
|
121
|
+
launched: string | null;
|
|
122
|
+
launchedPath: string | null;
|
|
123
|
+
force: boolean;
|
|
124
|
+
}): NpmSourceLaunchVerdict;
|
|
88
125
|
export declare function performVersionedUpgrade(component: UpgradeComponent, opts: VersionedUpgradeOptions): Promise<void>;
|
|
89
126
|
export {};
|
|
90
127
|
//# sourceMappingURL=versioned-upgrade.d.ts.map
|
package/dist/cli/kici-admin.d.ts
CHANGED
|
@@ -19,4 +19,17 @@ import { Command } from 'commander';
|
|
|
19
19
|
export declare function buildProgram(): Command;
|
|
20
20
|
/** Build the program and parse argv — the bin-shim entry point. */
|
|
21
21
|
export declare function runCli(argv?: string[]): void;
|
|
22
|
+
/**
|
|
23
|
+
* Canonicalize a path through any symlinks, falling back to a plain `resolve`
|
|
24
|
+
* when the target does not exist on disk. The entry-point guard compares the
|
|
25
|
+
* invoked script path against this module's own path; both sides must be fully
|
|
26
|
+
* canonicalized or a symlinked invocation path silently fails the match. The
|
|
27
|
+
* canonical example is the light-package launcher running
|
|
28
|
+
* `node /tmp/.../kici-admin.cjs` on macOS, where `/tmp` is a symlink to
|
|
29
|
+
* `/private/tmp`: `process.argv[1]` keeps the `/tmp` form while the bundle's
|
|
30
|
+
* `import.meta.url` resolves to the real `/private/tmp` path, so an
|
|
31
|
+
* un-canonicalized comparison is always false and the CLI exits 0 without
|
|
32
|
+
* running any command.
|
|
33
|
+
*/
|
|
34
|
+
export declare function canonicalize(p: string): string;
|
|
22
35
|
//# sourceMappingURL=kici-admin.d.ts.map
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* a container-based service management backend that works on any
|
|
7
7
|
* platform with Docker or Podman installed.
|
|
8
8
|
*/
|
|
9
|
-
import type { ServiceManager, ServiceConfig, ServiceStatus, LogOptions, DiscoveredInstance } from './types.js';
|
|
9
|
+
import type { ServiceManager, ServiceConfig, ServiceStatus, LogOptions, DiscoveredInstance, LaunchSpec } from './types.js';
|
|
10
10
|
export declare class ComposeServiceManager implements ServiceManager {
|
|
11
11
|
private runtime;
|
|
12
12
|
/** Get the runtime binary (`podman` or `docker`), detecting it if not already done. */
|
|
@@ -22,5 +22,6 @@ export declare class ComposeServiceManager implements ServiceManager {
|
|
|
22
22
|
logs(config: ServiceConfig, options: LogOptions): Promise<void>;
|
|
23
23
|
isInstalled(config: ServiceConfig): Promise<boolean>;
|
|
24
24
|
list(isUserLevel: boolean): Promise<DiscoveredInstance[]>;
|
|
25
|
+
readLaunchSpec(_config: ServiceConfig): Promise<LaunchSpec | null>;
|
|
25
26
|
}
|
|
26
27
|
//# sourceMappingURL=compose.d.ts.map
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
* specified) platform. Uses dynamic imports to avoid loading all
|
|
7
7
|
* platform-specific code on every platform.
|
|
8
8
|
*/
|
|
9
|
-
export type { ServiceManager, ServiceConfig, ServiceStatus, ServiceState, ServicePlatform, RestartPolicy, LogOptions, } from './types.js';
|
|
9
|
+
export type { ServiceManager, ServiceConfig, LaunchSpec, ServiceStatus, ServiceState, ServicePlatform, RestartPolicy, LogOptions, } from './types.js';
|
|
10
10
|
export { DEFAULT_RESTART_POLICY } from './types.js';
|
|
11
11
|
export { detectPlatform, isRoot, kiciConfigRoot, getConfigDir, getLogDir, getCacheDir, } from './platform-detect.js';
|
|
12
12
|
export { resolveUserLevel, type PrivilegeOpts } from './privilege.js';
|
|
13
13
|
export type { Component, InstanceManifest, IndexEntry, ResolveOptions, ResolvedInstance, } from './instance/types.js';
|
|
14
14
|
export { COMPONENTS, isComponent } from './instance/types.js';
|
|
15
|
-
export { manifestFilename, manifestPath, readManifest, writeManifest, readKiciVersion, } from './instance/manifest.js';
|
|
15
|
+
export { manifestFilename, manifestPath, readManifest, writeManifest, readKiciVersion, resolveVersionFromLaunchSpec, } from './instance/manifest.js';
|
|
16
16
|
export { indexPath, readIndex, writeIndex, appendIndexEntry, removeIndexEntry, } from './instance/index-file.js';
|
|
17
17
|
export { listInstances, resolveInstance, formatRefusal } from './instance/resolve.js';
|
|
18
18
|
export type { ListedInstance, ResolveArgs, ListInstancesArgs } from './instance/resolve.js';
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* every lifecycle command to reconstruct the ServiceConfig.
|
|
5
5
|
*/
|
|
6
6
|
import type { Component, InstanceManifest } from './types.js';
|
|
7
|
+
import type { LaunchSpec } from '../types.js';
|
|
7
8
|
/** Per-component manifest filename. */
|
|
8
9
|
export declare function manifestFilename(component: Component): string;
|
|
9
10
|
/** Resolve the manifest path inside an instance directory. */
|
|
@@ -27,4 +28,15 @@ export declare function writeManifest(instanceDir: string, manifest: InstanceMan
|
|
|
27
28
|
* reliable source.
|
|
28
29
|
*/
|
|
29
30
|
export declare function readKiciVersion(): string;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the `@kici-dev/<component>` package version that a {@link LaunchSpec}
|
|
33
|
+
* will actually execute, by locating the component's entry script among the
|
|
34
|
+
* spec's tokens and reading the version from the package.json that owns it.
|
|
35
|
+
*
|
|
36
|
+
* Returns null when no resolvable entry script is present (e.g. an opaque
|
|
37
|
+
* `--binary` install) or the owning package.json is missing / name-mismatched.
|
|
38
|
+
* This is what lets the npm-source upgrade refuse to report a version it can't
|
|
39
|
+
* stand behind.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveVersionFromLaunchSpec(spec: LaunchSpec, component: Component): string | null;
|
|
30
42
|
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -27,8 +27,18 @@ export interface ListInstancesArgs {
|
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Reconcile <kiciRoot>/instances.json with the driver's native scan, then
|
|
30
|
-
* rewrite the index
|
|
31
|
-
*
|
|
30
|
+
* rewrite the index so it mirrors the scanned instances that have a known
|
|
31
|
+
* instanceDir. Two self-heal directions happen here:
|
|
32
|
+
*
|
|
33
|
+
* - backward: drop index entries whose unit no longer exists.
|
|
34
|
+
* - forward: adopt an instanceDir the driver recovered from the unit marker
|
|
35
|
+
* when the index entry is missing or carries no dir — so a lost or emptied
|
|
36
|
+
* index rebuilds itself from the init system on the next read.
|
|
37
|
+
*
|
|
38
|
+
* The init system is therefore the source of truth for the name→folder mapping
|
|
39
|
+
* (via the X-KiCI-InstanceDir / KiCIInstanceDir / dev.kici.instance-dir / [KiCI-DIR]
|
|
40
|
+
* markers), and the index is a pure cache. Returns the merged list filtered to
|
|
41
|
+
* the requested component + isUserLevel.
|
|
32
42
|
*/
|
|
33
43
|
export declare function listInstances(args: ListInstancesArgs): Promise<ListedInstance[]>;
|
|
34
44
|
export interface ResolveArgs {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Supports both system-level (/Library/LaunchDaemons/) and
|
|
6
6
|
* user-level (~/Library/LaunchAgents/) agents.
|
|
7
7
|
*/
|
|
8
|
-
import type { ServiceConfig, ServiceManager, ServiceStatus, LogOptions, DiscoveredInstance } from './types.js';
|
|
8
|
+
import type { ServiceConfig, ServiceManager, ServiceStatus, LogOptions, DiscoveredInstance, LaunchSpec } from './types.js';
|
|
9
9
|
export declare class LaunchdServiceManager implements ServiceManager {
|
|
10
10
|
/** Build the launchd label for a service. */
|
|
11
11
|
private label;
|
|
@@ -17,6 +17,8 @@ export declare class LaunchdServiceManager implements ServiceManager {
|
|
|
17
17
|
private parseEnvFile;
|
|
18
18
|
/** Escape XML special characters. */
|
|
19
19
|
private escapeXml;
|
|
20
|
+
/** Inverse of {@link escapeXml} — decode the entities back to literals. */
|
|
21
|
+
private unescapeXml;
|
|
20
22
|
/**
|
|
21
23
|
* Generate a launchd plist XML string from a service config.
|
|
22
24
|
* Visible for testing.
|
|
@@ -67,5 +69,6 @@ export declare class LaunchdServiceManager implements ServiceManager {
|
|
|
67
69
|
logs(config: ServiceConfig, options: LogOptions): Promise<void>;
|
|
68
70
|
isInstalled(config: ServiceConfig): Promise<boolean>;
|
|
69
71
|
list(isUserLevel: boolean): Promise<DiscoveredInstance[]>;
|
|
72
|
+
readLaunchSpec(config: ServiceConfig): Promise<LaunchSpec | null>;
|
|
70
73
|
}
|
|
71
74
|
//# sourceMappingURL=launchd.d.ts.map
|