@solcreek/cli 0.4.21 → 0.4.22
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/CHANGELOG.md +21 -0
- package/dist/commands/dashboard.d.ts +21 -0
- package/dist/commands/dashboard.js +72 -0
- package/dist/commands/deploy.d.ts +10 -0
- package/dist/commands/deploy.js +252 -0
- package/dist/commands/dev.d.ts +13 -0
- package/dist/commands/dev.js +77 -2
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.js +158 -2
- package/dist/commands/logs.d.ts +12 -0
- package/dist/commands/logs.js +69 -1
- package/dist/commands/restart.d.ts +26 -0
- package/dist/commands/restart.js +55 -0
- package/dist/commands/rollback.d.ts +13 -0
- package/dist/commands/rollback.js +188 -1
- package/dist/commands/stop.d.ts +26 -0
- package/dist/commands/stop.js +65 -0
- package/dist/commands/top.d.ts +28 -0
- package/dist/commands/top.js +171 -0
- package/dist/dev/creekd-runner.d.ts +22 -0
- package/dist/dev/creekd-runner.js +188 -0
- package/dist/index.js +8 -0
- package/dist/utils/creekd-client.d.ts +152 -0
- package/dist/utils/creekd-client.js +144 -0
- package/dist/utils/gitignore.d.ts +2 -0
- package/dist/utils/gitignore.js +32 -0
- package/dist/utils/hostkey.d.ts +39 -0
- package/dist/utils/hostkey.js +84 -0
- package/dist/utils/hosts.d.ts +70 -0
- package/dist/utils/hosts.js +90 -0
- package/dist/utils/local-cache.d.ts +69 -0
- package/dist/utils/local-cache.js +100 -0
- package/dist/utils/nextjs.d.ts +4 -2
- package/dist/utils/nextjs.js +107 -38
- package/dist/utils/prepare-bundle.js +1 -1
- package/dist/utils/top-format.d.ts +4 -0
- package/dist/utils/top-format.js +32 -0
- package/dist/utils/watch.d.ts +81 -0
- package/dist/utils/watch.js +87 -0
- package/package.json +2 -2
package/dist/utils/nextjs.js
CHANGED
|
@@ -40,27 +40,38 @@ function semverGte(version, target) {
|
|
|
40
40
|
return aPat >= bPat;
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
43
|
+
* Resolve @solcreek/adapter-creek from any reachable location.
|
|
44
44
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* Tries, in order: the CLI's own install (monorepo workspace / global
|
|
46
|
+
* install alongside the adapter), the project's own node_modules, then the
|
|
47
|
+
* lazy-installed copy under .creek/node_modules. Returns the adapter entry
|
|
48
|
+
* path (for NEXT_ADAPTER_PATH), or null if not installed anywhere.
|
|
48
49
|
*/
|
|
49
|
-
function resolveAdapterPath() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
function resolveAdapterPath(cwd) {
|
|
51
|
+
const bases = [import.meta.url];
|
|
52
|
+
if (cwd) {
|
|
53
|
+
// createRequire walks node_modules up from the base file's directory;
|
|
54
|
+
// the base file itself need not exist.
|
|
55
|
+
bases.push(join(cwd, "package.json"));
|
|
56
|
+
bases.push(join(cwd, CREEK_DIR, "package.json"));
|
|
53
57
|
}
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
for (const base of bases) {
|
|
59
|
+
try {
|
|
60
|
+
return createRequire(base).resolve("@solcreek/adapter-creek");
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// try next base
|
|
64
|
+
}
|
|
56
65
|
}
|
|
66
|
+
return null;
|
|
57
67
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Build a Next.js app using the Creek adapter (>= 16.2.3).
|
|
70
|
+
*
|
|
71
|
+
* Sets NEXT_ADAPTER_PATH to the resolved adapter. No opennext, no wrangler,
|
|
72
|
+
* no config patching — the adapter handles everything inside onBuildComplete().
|
|
73
|
+
*/
|
|
74
|
+
function buildWithAdapter(cwd, adapterPath) {
|
|
64
75
|
consola.start(" Building Next.js with Creek adapter...\n");
|
|
65
76
|
// --webpack is required: Turbopack does not generate standalone output,
|
|
66
77
|
// and its chunked format uses a custom runtime incompatible with esbuild.
|
|
@@ -73,24 +84,28 @@ function buildWithAdapter(cwd) {
|
|
|
73
84
|
/**
|
|
74
85
|
* Unified Next.js build entry point.
|
|
75
86
|
*
|
|
76
|
-
* - Next.js >= 16.2.3: Creek adapter path (recommended)
|
|
77
|
-
*
|
|
87
|
+
* - Next.js >= 16.2.3: Creek adapter path (recommended). The adapter is
|
|
88
|
+
* lazily installed into .creek/node_modules on first use — the CLI never
|
|
89
|
+
* depends on it directly, so non-Next.js users never pay for it.
|
|
90
|
+
* - Next.js < 16.2.3 (or adapter install fails): legacy opennext path.
|
|
78
91
|
*
|
|
79
92
|
* Min version for the adapter path matches @solcreek/adapter-creek's
|
|
80
93
|
* peerDependency, which pins Next.js >= 16.2.3 to fix CVE-2026-23869.
|
|
81
94
|
*/
|
|
82
95
|
export function buildNextjs(cwd, isMonorepo, projectName) {
|
|
83
96
|
const version = getNextVersion(cwd);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (version) {
|
|
90
|
-
consola.warn(` Next.js ${version} — using legacy build path`);
|
|
97
|
+
if (version && semverGte(version, "16.2.3")) {
|
|
98
|
+
const adapterPath = ensureAdapter(cwd);
|
|
99
|
+
if (adapterPath) {
|
|
100
|
+
buildWithAdapter(cwd, adapterPath);
|
|
101
|
+
return;
|
|
91
102
|
}
|
|
92
|
-
|
|
103
|
+
consola.warn(` Falling back to legacy build path for Next.js ${version}`);
|
|
93
104
|
}
|
|
105
|
+
else if (version) {
|
|
106
|
+
consola.warn(` Next.js ${version} — using legacy build path`);
|
|
107
|
+
}
|
|
108
|
+
buildNextjsForWorkers(cwd, isMonorepo, projectName);
|
|
94
109
|
}
|
|
95
110
|
/** Check if the adapter output exists (vs legacy opennext output). */
|
|
96
111
|
export function hasAdapterOutput(cwd) {
|
|
@@ -132,6 +147,71 @@ export function patchBundledWorker(bundleDir, openNextDir) {
|
|
|
132
147
|
const CREEK_DIR = ".creek";
|
|
133
148
|
const OPENNEXT_PKG = "@opennextjs/cloudflare";
|
|
134
149
|
const OPENNEXT_VERSION = "^1.18.0";
|
|
150
|
+
const ADAPTER_PKG = "@solcreek/adapter-creek";
|
|
151
|
+
const ADAPTER_VERSION = "^0.2.0";
|
|
152
|
+
/**
|
|
153
|
+
* Merge a dependency into .creek/package.json without clobbering deps that
|
|
154
|
+
* a previous install (adapter or opennext) may have already written.
|
|
155
|
+
*/
|
|
156
|
+
function upsertCreekDep(creekDir, pkg, version) {
|
|
157
|
+
const pkgPath = join(creekDir, "package.json");
|
|
158
|
+
let manifest = {
|
|
159
|
+
private: true,
|
|
160
|
+
dependencies: {},
|
|
161
|
+
};
|
|
162
|
+
if (existsSync(pkgPath)) {
|
|
163
|
+
try {
|
|
164
|
+
manifest = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
165
|
+
manifest.dependencies ??= {};
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
manifest = { private: true, dependencies: {} };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
manifest.dependencies[pkg] = version;
|
|
172
|
+
writeFileSync(pkgPath, JSON.stringify(manifest, null, 2));
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Install a package into .creek/node_modules. Returns false if npm fails.
|
|
176
|
+
*/
|
|
177
|
+
function installCreekDep(creekDir, pkg, version) {
|
|
178
|
+
mkdirSync(creekDir, { recursive: true });
|
|
179
|
+
upsertCreekDep(creekDir, pkg, version);
|
|
180
|
+
try {
|
|
181
|
+
execSync("npm install --no-audit --no-fund --ignore-scripts --no-optional", {
|
|
182
|
+
cwd: creekDir,
|
|
183
|
+
stdio: "pipe",
|
|
184
|
+
});
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Ensure @solcreek/adapter-creek is resolvable, lazily installing it into
|
|
193
|
+
* .creek/node_modules on demand. Returns the resolved adapter entry path
|
|
194
|
+
* (for NEXT_ADAPTER_PATH), or null if it could not be made available.
|
|
195
|
+
*
|
|
196
|
+
* Lazy by design: the CLI stays framework-neutral, so the adapter — a
|
|
197
|
+
* Next.js-specific package with its own Next peerDependency — is only
|
|
198
|
+
* fetched when a Next.js project is actually deployed. It is never a hard
|
|
199
|
+
* CLI dependency that every `npx creek` user would pay for.
|
|
200
|
+
*/
|
|
201
|
+
function ensureAdapter(cwd) {
|
|
202
|
+
const existing = resolveAdapterPath(cwd);
|
|
203
|
+
if (existing)
|
|
204
|
+
return existing;
|
|
205
|
+
consola.start(` Installing ${ADAPTER_PKG} (one-time setup)...`);
|
|
206
|
+
if (!installCreekDep(join(cwd, CREEK_DIR), ADAPTER_PKG, ADAPTER_VERSION)) {
|
|
207
|
+
consola.warn(` Could not install ${ADAPTER_PKG}`);
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
const resolved = resolveAdapterPath(cwd);
|
|
211
|
+
if (resolved)
|
|
212
|
+
consola.success(` ${ADAPTER_PKG} installed`);
|
|
213
|
+
return resolved;
|
|
214
|
+
}
|
|
135
215
|
/**
|
|
136
216
|
* Ensure @opennextjs/cloudflare is available in .creek/node_modules.
|
|
137
217
|
* Returns the path to the opennextjs-cloudflare CLI binary.
|
|
@@ -142,18 +222,7 @@ function ensureOpenNext(cwd) {
|
|
|
142
222
|
if (existsSync(opennextBin))
|
|
143
223
|
return opennextBin;
|
|
144
224
|
consola.start(` Installing ${OPENNEXT_PKG} (one-time setup)...`);
|
|
145
|
-
|
|
146
|
-
const pkgPath = join(creekDir, "package.json");
|
|
147
|
-
if (!existsSync(pkgPath)) {
|
|
148
|
-
writeFileSync(pkgPath, JSON.stringify({
|
|
149
|
-
private: true,
|
|
150
|
-
dependencies: { [OPENNEXT_PKG]: OPENNEXT_VERSION },
|
|
151
|
-
}, null, 2));
|
|
152
|
-
}
|
|
153
|
-
execSync("npm install --no-audit --no-fund --ignore-scripts --no-optional", {
|
|
154
|
-
cwd: creekDir,
|
|
155
|
-
stdio: "pipe",
|
|
156
|
-
});
|
|
225
|
+
installCreekDep(creekDir, OPENNEXT_PKG, OPENNEXT_VERSION);
|
|
157
226
|
consola.success(` ${OPENNEXT_PKG} installed`);
|
|
158
227
|
return opennextBin;
|
|
159
228
|
}
|
|
@@ -82,7 +82,7 @@ export async function prepareDeployBundle(input) {
|
|
|
82
82
|
const useAdapterOutput = framework === "nextjs" && hasAdapterOutput(cwd);
|
|
83
83
|
const outputDir = useAdapterOutput
|
|
84
84
|
? resolve(cwd, ".creek/adapter-output")
|
|
85
|
-
: resolve(cwd, resolved.buildOutput || getDefaultBuildOutput(framework));
|
|
85
|
+
: resolve(cwd, resolved.buildOutput || getDefaultBuildOutput(framework, cwd));
|
|
86
86
|
// 4. Post-build framework adapter detection. Astro can be either SSG
|
|
87
87
|
// or CF-adapter-SSR; we only know which after build.
|
|
88
88
|
const astroAdapter = framework === "astro" ? detectAstroCloudflareBuild(cwd) : null;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function fmtBytes(bytes: number): string;
|
|
2
|
+
export declare function fmtDuration(ms: number): string;
|
|
3
|
+
export declare function calcCpuPercent(prevUsec: number, prevTs: number, currUsec: number, currTs: number): number | null;
|
|
4
|
+
//# sourceMappingURL=top-format.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function fmtBytes(bytes) {
|
|
2
|
+
if (bytes < 1024)
|
|
3
|
+
return bytes + "B";
|
|
4
|
+
if (bytes < 1024 * 1024)
|
|
5
|
+
return (bytes / 1024).toFixed(1) + "K";
|
|
6
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
7
|
+
return (bytes / (1024 * 1024)).toFixed(1) + "M";
|
|
8
|
+
return (bytes / (1024 * 1024 * 1024)).toFixed(1) + "G";
|
|
9
|
+
}
|
|
10
|
+
export function fmtDuration(ms) {
|
|
11
|
+
if (ms < 1000)
|
|
12
|
+
return "0s";
|
|
13
|
+
const s = Math.floor(ms / 1000);
|
|
14
|
+
if (s < 60)
|
|
15
|
+
return s + "s";
|
|
16
|
+
const m = Math.floor(s / 60);
|
|
17
|
+
if (m < 60)
|
|
18
|
+
return m + "m" + (s % 60 > 0 ? (s % 60) + "s" : "");
|
|
19
|
+
const h = Math.floor(m / 60);
|
|
20
|
+
if (h < 24)
|
|
21
|
+
return h + "h" + (m % 60 > 0 ? (m % 60) + "m" : "");
|
|
22
|
+
const d = Math.floor(h / 24);
|
|
23
|
+
return d + "d" + (h % 24 > 0 ? (h % 24) + "h" : "");
|
|
24
|
+
}
|
|
25
|
+
export function calcCpuPercent(prevUsec, prevTs, currUsec, currTs) {
|
|
26
|
+
const dtMs = currTs - prevTs;
|
|
27
|
+
if (dtMs <= 0)
|
|
28
|
+
return null;
|
|
29
|
+
const dtUs = currUsec - prevUsec;
|
|
30
|
+
return (dtUs / 1000 / dtMs) * 100;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=top-format.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Poll `GET /v1/apps/{id}` until the app reaches a terminal
|
|
3
|
+
* deploy state: Ready=True + Progressing=False (success), or
|
|
4
|
+
* Degraded=True reason=DeployTimeout (the daemon's own
|
|
5
|
+
* progressing_timeout has flipped — surfaces here as
|
|
6
|
+
* deploy_stuck per DESIGN-self-host-state.md §"progressing_timeout
|
|
7
|
+
* uses monotonic clock"), or the client-side watch budget runs
|
|
8
|
+
* out.
|
|
9
|
+
*
|
|
10
|
+
* This is the consumer of #10's observedGeneration / conditions
|
|
11
|
+
* machinery and #8a's status.conditions[] surface. The whole
|
|
12
|
+
* point of those server-side primitives is exactly THIS loop:
|
|
13
|
+
* a watcher polling GET, inspecting conditions[], deciding
|
|
14
|
+
* "still progressing vs converged vs stuck" without needing a
|
|
15
|
+
* separate event stream.
|
|
16
|
+
*
|
|
17
|
+
* `watchDeploy` is pure (modulo fetch + setTimeout) — the
|
|
18
|
+
* test harness drives it through synthetic state transitions
|
|
19
|
+
* by sequencing mock responses.
|
|
20
|
+
*/
|
|
21
|
+
import type { CreekdClient, AppEnvelope } from "./creekd-client.js";
|
|
22
|
+
/** Terminal outcome of a watch loop. */
|
|
23
|
+
export type WatchResult = {
|
|
24
|
+
ok: true;
|
|
25
|
+
reason: "ready";
|
|
26
|
+
envelope: AppEnvelope;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
reason: "deploy_stuck";
|
|
30
|
+
envelope: AppEnvelope;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
reason: "watch_timeout";
|
|
34
|
+
elapsedMs: number;
|
|
35
|
+
lastEnvelope?: AppEnvelope;
|
|
36
|
+
} | {
|
|
37
|
+
ok: false;
|
|
38
|
+
reason: "fetch_failed";
|
|
39
|
+
error: Error;
|
|
40
|
+
};
|
|
41
|
+
export interface WatchOptions {
|
|
42
|
+
/** Milliseconds between polls. Default 1000. Clamped to >=100. */
|
|
43
|
+
pollIntervalMs?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Maximum total wall time the watch will hang before returning
|
|
46
|
+
* watch_timeout. Default 5 min. Independent of the daemon's
|
|
47
|
+
* progressing_timeout — the client's bound is "user's patience"
|
|
48
|
+
* not "server's deploy budget"; the two normally agree.
|
|
49
|
+
*/
|
|
50
|
+
timeoutMs?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Injected clock + sleep for tests. Production callers omit; the
|
|
53
|
+
* defaults are Date.now + setTimeout-based delay.
|
|
54
|
+
*/
|
|
55
|
+
now?: () => number;
|
|
56
|
+
sleep?: (ms: number) => Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Optional progress callback invoked after each poll. Tests use
|
|
59
|
+
* this to assert intermediate state; production callers can
|
|
60
|
+
* stream it to a spinner.
|
|
61
|
+
*/
|
|
62
|
+
onPoll?: (envelope: AppEnvelope, elapsedMs: number) => void;
|
|
63
|
+
}
|
|
64
|
+
export declare function watchDeploy(client: CreekdClient, appId: string, opts?: WatchOptions): Promise<WatchResult>;
|
|
65
|
+
type Verdict = "ready" | "deploy_stuck" | "progressing" | "unknown";
|
|
66
|
+
/**
|
|
67
|
+
* Inspect a single envelope's status.conditions[] and decide
|
|
68
|
+
* whether the watch loop is done.
|
|
69
|
+
*
|
|
70
|
+
* Ready=True AND Progressing=False → ready (success)
|
|
71
|
+
* Degraded=True AND reason=DeployTimeout → deploy_stuck (DESIGN code)
|
|
72
|
+
* Progressing=True (any reason) → progressing (keep polling)
|
|
73
|
+
* anything else → unknown (keep polling — server
|
|
74
|
+
* hasn't classified yet)
|
|
75
|
+
*
|
|
76
|
+
* Exported for unit-test visibility; not part of the consumer
|
|
77
|
+
* API.
|
|
78
|
+
*/
|
|
79
|
+
export declare function classifyConditions(envelope: AppEnvelope): Verdict;
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=watch.d.ts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Poll `GET /v1/apps/{id}` until the app reaches a terminal
|
|
3
|
+
* deploy state: Ready=True + Progressing=False (success), or
|
|
4
|
+
* Degraded=True reason=DeployTimeout (the daemon's own
|
|
5
|
+
* progressing_timeout has flipped — surfaces here as
|
|
6
|
+
* deploy_stuck per DESIGN-self-host-state.md §"progressing_timeout
|
|
7
|
+
* uses monotonic clock"), or the client-side watch budget runs
|
|
8
|
+
* out.
|
|
9
|
+
*
|
|
10
|
+
* This is the consumer of #10's observedGeneration / conditions
|
|
11
|
+
* machinery and #8a's status.conditions[] surface. The whole
|
|
12
|
+
* point of those server-side primitives is exactly THIS loop:
|
|
13
|
+
* a watcher polling GET, inspecting conditions[], deciding
|
|
14
|
+
* "still progressing vs converged vs stuck" without needing a
|
|
15
|
+
* separate event stream.
|
|
16
|
+
*
|
|
17
|
+
* `watchDeploy` is pure (modulo fetch + setTimeout) — the
|
|
18
|
+
* test harness drives it through synthetic state transitions
|
|
19
|
+
* by sequencing mock responses.
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_POLL_MS = 1000;
|
|
22
|
+
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
23
|
+
const MIN_POLL_MS = 100;
|
|
24
|
+
export async function watchDeploy(client, appId, opts = {}) {
|
|
25
|
+
const pollMs = Math.max(MIN_POLL_MS, opts.pollIntervalMs ?? DEFAULT_POLL_MS);
|
|
26
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
27
|
+
const now = opts.now ?? Date.now;
|
|
28
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
29
|
+
const start = now();
|
|
30
|
+
let lastEnvelope;
|
|
31
|
+
while (true) {
|
|
32
|
+
const elapsedMs = now() - start;
|
|
33
|
+
if (elapsedMs > timeoutMs) {
|
|
34
|
+
return { ok: false, reason: "watch_timeout", elapsedMs, lastEnvelope };
|
|
35
|
+
}
|
|
36
|
+
let envelope;
|
|
37
|
+
try {
|
|
38
|
+
envelope = await client.getApp(appId);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
return { ok: false, reason: "fetch_failed", error: e instanceof Error ? e : new Error(String(e)) };
|
|
42
|
+
}
|
|
43
|
+
lastEnvelope = envelope;
|
|
44
|
+
opts.onPoll?.(envelope, elapsedMs);
|
|
45
|
+
const verdict = classifyConditions(envelope);
|
|
46
|
+
if (verdict === "ready")
|
|
47
|
+
return { ok: true, reason: "ready", envelope };
|
|
48
|
+
if (verdict === "deploy_stuck")
|
|
49
|
+
return { ok: false, reason: "deploy_stuck", envelope };
|
|
50
|
+
// "progressing" or "unknown" → keep polling.
|
|
51
|
+
await sleep(pollMs);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Inspect a single envelope's status.conditions[] and decide
|
|
56
|
+
* whether the watch loop is done.
|
|
57
|
+
*
|
|
58
|
+
* Ready=True AND Progressing=False → ready (success)
|
|
59
|
+
* Degraded=True AND reason=DeployTimeout → deploy_stuck (DESIGN code)
|
|
60
|
+
* Progressing=True (any reason) → progressing (keep polling)
|
|
61
|
+
* anything else → unknown (keep polling — server
|
|
62
|
+
* hasn't classified yet)
|
|
63
|
+
*
|
|
64
|
+
* Exported for unit-test visibility; not part of the consumer
|
|
65
|
+
* API.
|
|
66
|
+
*/
|
|
67
|
+
export function classifyConditions(envelope) {
|
|
68
|
+
const conds = envelope.status?.conditions ?? [];
|
|
69
|
+
const ready = conds.find((c) => c.type === "Ready");
|
|
70
|
+
const progressing = conds.find((c) => c.type === "Progressing");
|
|
71
|
+
const degraded = conds.find((c) => c.type === "Degraded");
|
|
72
|
+
// deploy_stuck has highest priority — even if Ready=True somehow,
|
|
73
|
+
// a DeployTimeout-flagged Degraded means the daemon gave up on
|
|
74
|
+
// this generation's convergence and the client should report
|
|
75
|
+
// failure rather than racing the wire.
|
|
76
|
+
if (degraded?.status === "True" && degraded.reason === "DeployTimeout") {
|
|
77
|
+
return "deploy_stuck";
|
|
78
|
+
}
|
|
79
|
+
if (ready?.status === "True" && progressing?.status === "False") {
|
|
80
|
+
return "ready";
|
|
81
|
+
}
|
|
82
|
+
if (progressing?.status === "True") {
|
|
83
|
+
return "progressing";
|
|
84
|
+
}
|
|
85
|
+
return "unknown";
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=watch.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solcreek/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.22",
|
|
4
4
|
"description": "CLI for the Creek deployment platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"esbuild": "^0.25.0",
|
|
34
34
|
"smol-toml": "^1.3.1",
|
|
35
35
|
"ws": "^8.20.0",
|
|
36
|
-
"@solcreek/sdk": "0.4.
|
|
36
|
+
"@solcreek/sdk": "0.4.10"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@testing-library/dom": "^10.4.1",
|