@p4code/cli 0.0.49 → 0.0.50
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/bin.mjs +311 -41
- package/dist/client/assets/{DiffPanel-Byg3lT0_.js → DiffPanel-BUvVWIMX.js} +2 -2
- package/dist/client/assets/{FilePreviewPanel-CRsMuFPM.js → FilePreviewPanel-Bk7x7hMd.js} +2 -2
- package/dist/client/assets/{PreviewPanel-DtVNEw5j.js → PreviewPanel-CR9iyxF2.js} +2 -2
- package/dist/client/assets/{fileCommentAnnotations-DEIuuBiQ.js → fileCommentAnnotations-OW7TQ0uO.js} +2 -2
- package/dist/client/assets/{index-CNTBEOVF.js → index-CHboeMs4.js} +4 -4
- package/dist/client/index.html +1 -1
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -55,6 +55,7 @@ import * as Exit from "effect/Exit";
|
|
|
55
55
|
import * as SchemaGetter from "effect/SchemaGetter";
|
|
56
56
|
import * as Config from "effect/Config";
|
|
57
57
|
import * as NodeReadline from "node:readline";
|
|
58
|
+
import * as Clock from "effect/Clock";
|
|
58
59
|
import * as Cache from "effect/Cache";
|
|
59
60
|
import * as Deferred from "effect/Deferred";
|
|
60
61
|
import * as Equal from "effect/Equal";
|
|
@@ -62,7 +63,6 @@ import * as Semaphore from "effect/Semaphore";
|
|
|
62
63
|
import * as Scope from "effect/Scope";
|
|
63
64
|
import { parse } from "yaml";
|
|
64
65
|
import * as HttpApiClient from "effect/unstable/httpapi/HttpApiClient";
|
|
65
|
-
import * as Clock from "effect/Clock";
|
|
66
66
|
import * as Metric from "effect/Metric";
|
|
67
67
|
import * as Queue from "effect/Queue";
|
|
68
68
|
import { cast, dual } from "effect/Function";
|
|
@@ -121,7 +121,7 @@ const closeServer = (server) => {
|
|
|
121
121
|
* NetService - Service tag for startup networking helpers.
|
|
122
122
|
*/
|
|
123
123
|
var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
|
|
124
|
-
const make$
|
|
124
|
+
const make$72 = () => {
|
|
125
125
|
/**
|
|
126
126
|
* Returns true when a TCP server can bind to {host, port}.
|
|
127
127
|
* `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
|
|
@@ -230,10 +230,10 @@ const make$71 = () => {
|
|
|
230
230
|
})
|
|
231
231
|
};
|
|
232
232
|
};
|
|
233
|
-
const layer$
|
|
233
|
+
const layer$68 = Layer.sync(NetService, make$72);
|
|
234
234
|
//#endregion
|
|
235
235
|
//#region package.json
|
|
236
|
-
var version = "0.0.
|
|
236
|
+
var version = "0.0.50";
|
|
237
237
|
//#endregion
|
|
238
238
|
//#region src/config.ts
|
|
239
239
|
/**
|
|
@@ -254,8 +254,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
|
|
|
254
254
|
/** @deprecated Import and use `layerTest` from this module. */
|
|
255
255
|
static layerTest = (cwd, baseDirOrPrefix) => layerTest$2(cwd, baseDirOrPrefix);
|
|
256
256
|
};
|
|
257
|
-
const make$
|
|
258
|
-
const layer$
|
|
257
|
+
const make$71 = (config) => ServerConfig$1.of(config);
|
|
258
|
+
const layer$67 = (config) => Layer.succeed(ServerConfig$1, make$71(config));
|
|
259
259
|
const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
|
|
260
260
|
const { join } = yield* Path.Path;
|
|
261
261
|
const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
|
|
@@ -869,7 +869,7 @@ function compareSemverVersions(left, right) {
|
|
|
869
869
|
* timeout, every failure swallowed, and never anything that changes an exit
|
|
870
870
|
* code. A version check that can break a command is worse than no check.
|
|
871
871
|
*/
|
|
872
|
-
const PACKAGE_NAME = "@p4code/cli";
|
|
872
|
+
const PACKAGE_NAME$1 = "@p4code/cli";
|
|
873
873
|
const CHECK_INTERVAL = Duration.hours(24);
|
|
874
874
|
const REQUEST_TIMEOUT = Duration.seconds(3);
|
|
875
875
|
/**
|
|
@@ -889,7 +889,7 @@ const encodeCacheEntry = Schema$1.encodeEffect(CacheEntryJson);
|
|
|
889
889
|
const NpmLatest = Schema$1.Struct({ version: Schema$1.optional(Schema$1.String) });
|
|
890
890
|
const fetchLatestVersion = Effect.fn("cli.updateCheck.fetch")(function* () {
|
|
891
891
|
const client = yield* HttpClient.HttpClient;
|
|
892
|
-
const request = HttpClientRequest.get(`https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`).pipe(HttpClientRequest.setHeader("accept", "application/json"));
|
|
892
|
+
const request = HttpClientRequest.get(`https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME$1)}/latest`).pipe(HttpClientRequest.setHeader("accept", "application/json"));
|
|
893
893
|
const response = yield* client.execute(request).pipe(Effect.timeout(REQUEST_TIMEOUT));
|
|
894
894
|
if (response.status < 200 || response.status >= 300) return null;
|
|
895
895
|
const version = (yield* response.json.pipe(Effect.flatMap(Schema$1.decodeUnknownEffect(NpmLatest)))).version?.trim();
|
|
@@ -923,7 +923,7 @@ const resolveLatestVersion = Effect.fn("cli.updateCheck.resolve")(function* (sta
|
|
|
923
923
|
* `service update` restarts it onto the new one.
|
|
924
924
|
*/
|
|
925
925
|
function formatUpdateReminder(input) {
|
|
926
|
-
return [`Update available: ${PACKAGE_NAME}@${input.latestVersion} (running ${input.currentVersion}).`, ` npm i -g ${PACKAGE_NAME}@latest && p4c service update`].join("\n");
|
|
926
|
+
return [`Update available: ${PACKAGE_NAME$1}@${input.latestVersion} (running ${input.currentVersion}).`, ` npm i -g ${PACKAGE_NAME$1}@latest && p4c service update`].join("\n");
|
|
927
927
|
}
|
|
928
928
|
/**
|
|
929
929
|
* The reminder line, or null when there is nothing to say. Callers print it;
|
|
@@ -8736,7 +8736,7 @@ function deriveAuthClientMetadata(input) {
|
|
|
8736
8736
|
//#endregion
|
|
8737
8737
|
//#region src/auth/EnvironmentAuthPolicy.ts
|
|
8738
8738
|
var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
|
|
8739
|
-
const make$
|
|
8739
|
+
const make$70 = Effect.gen(function* () {
|
|
8740
8740
|
const config = yield* ServerConfig$1;
|
|
8741
8741
|
const isRemoteReachable = isRemoteReachableHost(config.host);
|
|
8742
8742
|
const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
|
|
@@ -8754,7 +8754,7 @@ const make$69 = Effect.gen(function* () {
|
|
|
8754
8754
|
};
|
|
8755
8755
|
return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
|
|
8756
8756
|
});
|
|
8757
|
-
const layer$
|
|
8757
|
+
const layer$66 = Layer.effect(EnvironmentAuthPolicy, make$70);
|
|
8758
8758
|
//#endregion
|
|
8759
8759
|
//#region src/persistence/Errors.ts
|
|
8760
8760
|
function summarizeSchemaIssue(issue) {
|
|
@@ -8935,7 +8935,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
|
|
|
8935
8935
|
cause
|
|
8936
8936
|
});
|
|
8937
8937
|
}
|
|
8938
|
-
const make$
|
|
8938
|
+
const make$69 = Effect.gen(function* () {
|
|
8939
8939
|
const sql = yield* SqlClient.SqlClient;
|
|
8940
8940
|
const createSessionRow = SqlSchema.void({
|
|
8941
8941
|
Request: CreateAuthSessionInput,
|
|
@@ -9069,7 +9069,7 @@ const make$68 = Effect.gen(function* () {
|
|
|
9069
9069
|
setLastConnectedAt
|
|
9070
9070
|
};
|
|
9071
9071
|
});
|
|
9072
|
-
const layer$
|
|
9072
|
+
const layer$65 = Layer.effect(AuthSessionRepository, make$69);
|
|
9073
9073
|
//#endregion
|
|
9074
9074
|
//#region src/auth/ServerSecretStore.ts
|
|
9075
9075
|
const secretStoreErrorContext = {
|
|
@@ -9136,7 +9136,7 @@ const isSecretStoreError = Schema$1.is(SecretStoreError);
|
|
|
9136
9136
|
const isPlatformError = (value) => Predicate.isTagged(value, "PlatformError");
|
|
9137
9137
|
const isSecretAlreadyExistsError = (error) => "cause" in error && isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists";
|
|
9138
9138
|
var ServerSecretStore = class extends Context.Service()("@p4code/cli/auth/ServerSecretStore") {};
|
|
9139
|
-
const make$
|
|
9139
|
+
const make$68 = Effect.gen(function* () {
|
|
9140
9140
|
const crypto = yield* Crypto.Crypto;
|
|
9141
9141
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
9142
9142
|
const path = yield* Path.Path;
|
|
@@ -9206,7 +9206,7 @@ const make$67 = Effect.gen(function* () {
|
|
|
9206
9206
|
remove
|
|
9207
9207
|
});
|
|
9208
9208
|
});
|
|
9209
|
-
const layer$
|
|
9209
|
+
const layer$64 = Layer.effect(ServerSecretStore, make$68);
|
|
9210
9210
|
//#endregion
|
|
9211
9211
|
//#region src/auth/SessionStore.ts
|
|
9212
9212
|
var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
|
|
@@ -9444,7 +9444,7 @@ function toAuthClientSession(input) {
|
|
|
9444
9444
|
current: false
|
|
9445
9445
|
};
|
|
9446
9446
|
}
|
|
9447
|
-
const make$
|
|
9447
|
+
const make$67 = Effect.gen(function* () {
|
|
9448
9448
|
const crypto = yield* Crypto.Crypto;
|
|
9449
9449
|
const serverConfig = yield* ServerConfig$1;
|
|
9450
9450
|
const secretStore = yield* ServerSecretStore;
|
|
@@ -9758,7 +9758,7 @@ const make$66 = Effect.gen(function* () {
|
|
|
9758
9758
|
markDisconnected
|
|
9759
9759
|
});
|
|
9760
9760
|
});
|
|
9761
|
-
const layer$
|
|
9761
|
+
const layer$63 = Layer.effect(SessionStore, make$67).pipe(Layer.provideMerge(layer$65));
|
|
9762
9762
|
//#endregion
|
|
9763
9763
|
//#region src/persistence/AuthPairingLinks.ts
|
|
9764
9764
|
const AuthPairingLinkRecord = Schema$1.Struct({
|
|
@@ -9819,7 +9819,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
|
|
|
9819
9819
|
cause
|
|
9820
9820
|
});
|
|
9821
9821
|
}
|
|
9822
|
-
const make$
|
|
9822
|
+
const make$66 = Effect.gen(function* () {
|
|
9823
9823
|
const sql = yield* SqlClient.SqlClient;
|
|
9824
9824
|
const createPairingLinkRow = SqlSchema.void({
|
|
9825
9825
|
Request: CreateAuthPairingLinkInput,
|
|
@@ -9954,7 +9954,7 @@ const make$65 = Effect.gen(function* () {
|
|
|
9954
9954
|
getByCredential
|
|
9955
9955
|
};
|
|
9956
9956
|
});
|
|
9957
|
-
const layer$
|
|
9957
|
+
const layer$62 = Layer.effect(AuthPairingLinkRepository, make$66);
|
|
9958
9958
|
//#endregion
|
|
9959
9959
|
//#region src/auth/PairingGrantStore.ts
|
|
9960
9960
|
var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
|
|
@@ -10049,7 +10049,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
|
|
|
10049
10049
|
const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
10050
10050
|
const PAIRING_TOKEN_LENGTH = 12;
|
|
10051
10051
|
const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
|
|
10052
|
-
const make$
|
|
10052
|
+
const make$65 = Effect.gen(function* () {
|
|
10053
10053
|
const crypto = yield* Crypto.Crypto;
|
|
10054
10054
|
const config = yield* ServerConfig$1;
|
|
10055
10055
|
const pairingLinks = yield* AuthPairingLinkRepository;
|
|
@@ -10247,7 +10247,7 @@ const make$64 = Effect.gen(function* () {
|
|
|
10247
10247
|
consume
|
|
10248
10248
|
});
|
|
10249
10249
|
});
|
|
10250
|
-
const layer$
|
|
10250
|
+
const layer$61 = Layer.effect(PairingGrantStore, make$65).pipe(Layer.provideMerge(layer$62));
|
|
10251
10251
|
//#endregion
|
|
10252
10252
|
//#region src/persistence/Migrations/001_OrchestrationEvents.ts
|
|
10253
10253
|
var _001_OrchestrationEvents_default = Effect.gen(function* () {
|
|
@@ -12020,7 +12020,7 @@ function parseBearerToken(request) {
|
|
|
12020
12020
|
const token = header.slice(7).trim();
|
|
12021
12021
|
return token.length > 0 ? token : null;
|
|
12022
12022
|
}
|
|
12023
|
-
const make$
|
|
12023
|
+
const make$64 = Effect.gen(function* () {
|
|
12024
12024
|
const policy = yield* EnvironmentAuthPolicy;
|
|
12025
12025
|
const bootstrapCredentials = yield* PairingGrantStore;
|
|
12026
12026
|
const sessions = yield* SessionStore;
|
|
@@ -12215,9 +12215,9 @@ const make$63 = Effect.gen(function* () {
|
|
|
12215
12215
|
issueStartupPairingUrl
|
|
12216
12216
|
});
|
|
12217
12217
|
});
|
|
12218
|
-
const layer$
|
|
12219
|
-
const storageLayer = Layer.mergeAll(layer$
|
|
12220
|
-
const runtimeLayer = layer$
|
|
12218
|
+
const layer$60 = Layer.effect(EnvironmentAuth, make$64).pipe(Layer.provideMerge(layer$61), Layer.provideMerge(layer$63), Layer.provideMerge(layer$66));
|
|
12219
|
+
const storageLayer = Layer.mergeAll(layer$64, layerConfig);
|
|
12220
|
+
const runtimeLayer = layer$60.pipe(Layer.provideMerge(storageLayer));
|
|
12221
12221
|
//#endregion
|
|
12222
12222
|
//#region src/cliAuthFormat.ts
|
|
12223
12223
|
const newline = "\n";
|
|
@@ -12972,7 +12972,7 @@ const runWithEnvironmentAuth = (flags, run, options) => Effect.gen(function* ()
|
|
|
12972
12972
|
}
|
|
12973
12973
|
return yield* Effect.gen(function* () {
|
|
12974
12974
|
return yield* run(yield* EnvironmentAuth);
|
|
12975
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$
|
|
12975
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
12976
12976
|
});
|
|
12977
12977
|
const ttlFlag = Flag.string("ttl").pipe(Flag.withSchema(DurationFromString), Flag.withDescription("TTL, for example `5m`, `1h`, `30d`, or `15 minutes`."), Flag.optional);
|
|
12978
12978
|
const jsonFlag = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON instead of human-readable output."), Flag.withDefault(false));
|
|
@@ -13069,6 +13069,64 @@ const authCommand = Command.make("auth").pipe(Command.withDescription("Manage th
|
|
|
13069
13069
|
* @module HubConfig
|
|
13070
13070
|
*/
|
|
13071
13071
|
var HubConfig = class extends Context.Service()("@p4code/cli/hub/HubConfig") {};
|
|
13072
|
+
const FAILURE_WINDOW_MS = 6e4;
|
|
13073
|
+
const BLOCK_DURATION_MS = 6e4;
|
|
13074
|
+
const initialThrottleState = {
|
|
13075
|
+
windowStartMillis: 0,
|
|
13076
|
+
failures: 0,
|
|
13077
|
+
blockedUntilMillis: 0
|
|
13078
|
+
};
|
|
13079
|
+
/** Whether a *failing* request should be refused outright rather than checked. */
|
|
13080
|
+
const isBlocked = (state, nowMillis) => nowMillis < state.blockedUntilMillis;
|
|
13081
|
+
/** Seconds a caller should wait, for `retry-after`. Always at least one. */
|
|
13082
|
+
const retryAfterSeconds = (state, nowMillis) => Math.max(1, Math.ceil((state.blockedUntilMillis - nowMillis) / 1e3));
|
|
13083
|
+
/**
|
|
13084
|
+
* Count a failure, and start blocking once the window fills.
|
|
13085
|
+
*
|
|
13086
|
+
* The window is fixed rather than sliding: a sliding window needs the timestamp
|
|
13087
|
+
* of every attempt, which is unbounded memory for an unbounded attacker. A
|
|
13088
|
+
* fixed window lets a burst straddle a boundary and land up to twice the limit,
|
|
13089
|
+
* which for this purpose is a rounding error against the size of the search
|
|
13090
|
+
* space.
|
|
13091
|
+
*/
|
|
13092
|
+
const recordFailure = (state, nowMillis, limits = {
|
|
13093
|
+
maxFailures: 10,
|
|
13094
|
+
windowMillis: FAILURE_WINDOW_MS,
|
|
13095
|
+
blockMillis: BLOCK_DURATION_MS
|
|
13096
|
+
}) => {
|
|
13097
|
+
if (nowMillis - state.windowStartMillis >= limits.windowMillis) return {
|
|
13098
|
+
windowStartMillis: nowMillis,
|
|
13099
|
+
failures: 1,
|
|
13100
|
+
blockedUntilMillis: 0
|
|
13101
|
+
};
|
|
13102
|
+
const failures = state.failures + 1;
|
|
13103
|
+
return {
|
|
13104
|
+
windowStartMillis: state.windowStartMillis,
|
|
13105
|
+
failures,
|
|
13106
|
+
blockedUntilMillis: failures >= limits.maxFailures ? nowMillis + limits.blockMillis : state.blockedUntilMillis
|
|
13107
|
+
};
|
|
13108
|
+
};
|
|
13109
|
+
var HubAuthThrottle = class extends Context.Service()("@p4code/cli/hub/HubAuthThrottle") {};
|
|
13110
|
+
const DEFAULT_LIMITS = {
|
|
13111
|
+
maxFailures: 10,
|
|
13112
|
+
windowMillis: FAILURE_WINDOW_MS,
|
|
13113
|
+
blockMillis: BLOCK_DURATION_MS
|
|
13114
|
+
};
|
|
13115
|
+
const make$63 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
|
|
13116
|
+
const state = yield* Ref.make(initialThrottleState);
|
|
13117
|
+
return HubAuthThrottle.of({
|
|
13118
|
+
shouldRefuse: Effect.gen(function* () {
|
|
13119
|
+
const now = yield* Clock.currentTimeMillis;
|
|
13120
|
+
return isBlocked(yield* Ref.get(state), now);
|
|
13121
|
+
}),
|
|
13122
|
+
recordFailure: Effect.gen(function* () {
|
|
13123
|
+
const now = yield* Clock.currentTimeMillis;
|
|
13124
|
+
const next = yield* Ref.updateAndGet(state, (current) => recordFailure(current, now, limits));
|
|
13125
|
+
return retryAfterSeconds(next, now);
|
|
13126
|
+
})
|
|
13127
|
+
});
|
|
13128
|
+
});
|
|
13129
|
+
const layer$59 = Layer.effect(HubAuthThrottle, make$63());
|
|
13072
13130
|
//#endregion
|
|
13073
13131
|
//#region src/hub/HubAuth.ts
|
|
13074
13132
|
/**
|
|
@@ -13127,6 +13185,31 @@ var HubUnauthorizedError = class {
|
|
|
13127
13185
|
_tag = "HubUnauthorizedError";
|
|
13128
13186
|
};
|
|
13129
13187
|
/**
|
|
13188
|
+
* Too many failed attempts recently; this one was not even checked.
|
|
13189
|
+
*
|
|
13190
|
+
* Distinct from `HubUnauthorizedError` so the response can differ. A 429 is the
|
|
13191
|
+
* honest answer — the credential was not evaluated, so calling it invalid would
|
|
13192
|
+
* be a claim the hub did not make — and it tells a legitimate server retrying a
|
|
13193
|
+
* stale token to back off rather than hammer.
|
|
13194
|
+
*/
|
|
13195
|
+
var HubThrottledError = class {
|
|
13196
|
+
_tag = "HubThrottledError";
|
|
13197
|
+
retryAfterSeconds;
|
|
13198
|
+
constructor(retryAfterSeconds) {
|
|
13199
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
13200
|
+
}
|
|
13201
|
+
};
|
|
13202
|
+
const hubThrottledResponse = (retryAfterSeconds) => HttpServerResponse.jsonUnsafe({
|
|
13203
|
+
error: "too_many_hub_credential_failures",
|
|
13204
|
+
message: "Too many failed credential attempts. Try again shortly."
|
|
13205
|
+
}, {
|
|
13206
|
+
status: 429,
|
|
13207
|
+
headers: {
|
|
13208
|
+
"cache-control": "no-store",
|
|
13209
|
+
"retry-after": String(retryAfterSeconds)
|
|
13210
|
+
}
|
|
13211
|
+
});
|
|
13212
|
+
/**
|
|
13130
13213
|
* Fails with {@link HubUnauthorizedError} unless the request carries a
|
|
13131
13214
|
* configured bearer token. Route handlers run this first and let
|
|
13132
13215
|
* `respondToHubAuthFailure` turn the failure into a 401.
|
|
@@ -13134,11 +13217,17 @@ var HubUnauthorizedError = class {
|
|
|
13134
13217
|
const authenticateHubRequest = Effect.fn("HubAuth.authenticateHubRequest")(function* () {
|
|
13135
13218
|
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
13136
13219
|
const { tokenHashes } = yield* HubConfig;
|
|
13220
|
+
const throttle = yield* HubAuthThrottle;
|
|
13137
13221
|
const token = readBearerToken(request.headers.authorization);
|
|
13138
|
-
if (
|
|
13139
|
-
|
|
13140
|
-
|
|
13222
|
+
if (isAuthorizedHubToken(token, tokenHashes)) return;
|
|
13223
|
+
if (yield* throttle.shouldRefuse) {
|
|
13224
|
+
const retryAfter = yield* throttle.recordFailure;
|
|
13225
|
+
yield* Effect.logWarning("refused hub request without checking; too many recent failures", { reason: token.length === 0 ? "missing_bearer_token" : "unknown_token" });
|
|
13226
|
+
return yield* Effect.fail(new HubThrottledError(retryAfter));
|
|
13141
13227
|
}
|
|
13228
|
+
yield* throttle.recordFailure;
|
|
13229
|
+
yield* Effect.logWarning("rejected hub request with an unusable credential", { reason: token.length === 0 ? "missing_bearer_token" : "unknown_token" });
|
|
13230
|
+
return yield* Effect.fail(new HubUnauthorizedError());
|
|
13142
13231
|
});
|
|
13143
13232
|
const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
13144
13233
|
/**
|
|
@@ -13547,6 +13636,7 @@ const storeUnavailable = HttpServerResponse.jsonUnsafe({
|
|
|
13547
13636
|
*/
|
|
13548
13637
|
const respondToHubFailures = (handler) => handler.pipe(Effect.catch((cause) => {
|
|
13549
13638
|
if (cause._tag === "HubUnauthorizedError") return Effect.succeed(hubUnauthorizedResponse);
|
|
13639
|
+
if (cause._tag === "HubThrottledError") return Effect.succeed(hubThrottledResponse(cause.retryAfterSeconds));
|
|
13550
13640
|
if (cause._tag === "InvalidHubRequest") return Effect.succeed(badRequest(cause.detail));
|
|
13551
13641
|
return Effect.logWarning("hub request failed", { cause }).pipe(Effect.as(storeUnavailable));
|
|
13552
13642
|
}));
|
|
@@ -13942,7 +14032,7 @@ const announce = Layer.effectDiscard(Effect.gen(function* () {
|
|
|
13942
14032
|
});
|
|
13943
14033
|
if (isRemoteReachableHost(config.host)) yield* Effect.logWarning("Hub is reachable beyond loopback. The bearer token is the only control — repeated auth failures are not rate limited.", { host: config.host });
|
|
13944
14034
|
}));
|
|
13945
|
-
const makeHubLayer = HttpRouter.serve(layer$58).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
|
|
14035
|
+
const makeHubLayer = HttpRouter.serve(layer$58).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$59), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
|
|
13946
14036
|
const runHub = Layer.launch(makeHubLayer);
|
|
13947
14037
|
//#endregion
|
|
13948
14038
|
//#region src/atomicWrite.ts
|
|
@@ -16126,7 +16216,7 @@ const make$58 = Effect.gen(function* () {
|
|
|
16126
16216
|
const layer$53 = Layer.effect(AssetSync, make$58);
|
|
16127
16217
|
//#endregion
|
|
16128
16218
|
//#region src/cli/assets.ts
|
|
16129
|
-
const SyncServicesLive = layer$53.pipe(Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$
|
|
16219
|
+
const SyncServicesLive = layer$53.pipe(Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$64))), Layer.provideMerge(layer$64), Layer.provideMerge(FetchHttpClient.layer));
|
|
16130
16220
|
const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
16131
16221
|
const config = yield* resolveCliAuthConfig(flags, yield* GlobalFlag.LogLevel);
|
|
16132
16222
|
return yield* Effect.gen(function* () {
|
|
@@ -16134,7 +16224,7 @@ const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
|
16134
16224
|
sync: yield* AssetSync,
|
|
16135
16225
|
link: yield* HubLink
|
|
16136
16226
|
});
|
|
16137
|
-
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$
|
|
16227
|
+
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
|
|
16138
16228
|
});
|
|
16139
16229
|
const STATE_LABEL = {
|
|
16140
16230
|
synced: "synced",
|
|
@@ -24582,7 +24672,7 @@ const RESTART_DELAY = Duration.seconds(2);
|
|
|
24582
24672
|
/** Exact npm versions only — never dist-tags — so the acknowledgement names
|
|
24583
24673
|
the version that was actually installed. Also keeps the value safe to
|
|
24584
24674
|
pass to npm and embed in filesystem paths. */
|
|
24585
|
-
const EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
24675
|
+
const EXACT_VERSION_PATTERN$1 = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
24586
24676
|
function normalizeEntryPath(entryPath) {
|
|
24587
24677
|
return entryPath.replaceAll("\\", "/");
|
|
24588
24678
|
}
|
|
@@ -24698,7 +24788,7 @@ const make$50 = Effect.fn("cloud.server_self_update.make")(function* (options) {
|
|
|
24698
24788
|
if (capability === null) return yield* failWith("This server cannot update itself; relaunch it manually with the new version.");
|
|
24699
24789
|
const activeMethod = capability;
|
|
24700
24790
|
const targetVersion = input.targetVersion.trim();
|
|
24701
|
-
if (!EXACT_VERSION_PATTERN.test(targetVersion)) return yield* failWith(`'${targetVersion}' is not an exact p4 version.`);
|
|
24791
|
+
if (!EXACT_VERSION_PATTERN$1.test(targetVersion)) return yield* failWith(`'${targetVersion}' is not an exact p4 version.`);
|
|
24702
24792
|
if (yield* Ref.getAndSet(inFlight, true)) return yield* failWith("A server update is already in progress.");
|
|
24703
24793
|
return yield* Effect.gen(function* () {
|
|
24704
24794
|
const runtimePaths = yield* ensurePinnedRuntimeInstalled({
|
|
@@ -24727,6 +24817,7 @@ const make$50 = Effect.fn("cloud.server_self_update.make")(function* (options) {
|
|
|
24727
24817
|
const homeDir = env.HOME ?? "";
|
|
24728
24818
|
const unitPath = path.join(homeDir, ".config", "systemd", "user", BOOT_SERVICE_UNIT_FILE);
|
|
24729
24819
|
const previousUnit = yield* fs.readFileString(unitPath).pipe(Effect.mapError((cause) => failWith("Could not read the current systemd unit.", cause)));
|
|
24820
|
+
const serviceOptions = yield* readServiceOptions({ baseDir: serverConfig.baseDir }).pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, path));
|
|
24730
24821
|
const unit = renderBootServiceUnit({
|
|
24731
24822
|
nodePath: host.execPath,
|
|
24732
24823
|
p4EntryPath: runtimePaths.entryPath,
|
|
@@ -24734,7 +24825,7 @@ const make$50 = Effect.fn("cloud.server_self_update.make")(function* (options) {
|
|
|
24734
24825
|
logPath: path.join(serverConfig.logsDir, "boot-service.log"),
|
|
24735
24826
|
unitPath,
|
|
24736
24827
|
workingDirectory: homeDir,
|
|
24737
|
-
serviceEnvironment:
|
|
24828
|
+
serviceEnvironment: serviceEnvironmentFor(serviceOptions)
|
|
24738
24829
|
});
|
|
24739
24830
|
yield* writeUnitAtomically(unitPath, unit).pipe(Effect.mapError((cause) => failWith("Could not update the systemd unit.", cause)));
|
|
24740
24831
|
const reloadSystemd = Effect.fn("cloud.server_self_update.reload_systemd")(function* () {
|
|
@@ -26603,7 +26694,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
26603
26694
|
});
|
|
26604
26695
|
yield* Console.log(output);
|
|
26605
26696
|
}));
|
|
26606
|
-
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$
|
|
26697
|
+
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
|
|
26607
26698
|
return yield* Effect.gen(function* () {
|
|
26608
26699
|
const snapshot = yield* getOfflineSnapshot();
|
|
26609
26700
|
const orchestrationEngine = yield* OrchestrationEngineService;
|
|
@@ -26614,7 +26705,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
26614
26705
|
});
|
|
26615
26706
|
yield* Console.log(output);
|
|
26616
26707
|
}).pipe(Effect.provide(offlineRuntimeLayer));
|
|
26617
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$42).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$
|
|
26708
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$42).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
26618
26709
|
});
|
|
26619
26710
|
const projectAddCommand = Command.make("add", {
|
|
26620
26711
|
...projectLocationFlags,
|
|
@@ -43610,7 +43701,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () {
|
|
|
43610
43701
|
};
|
|
43611
43702
|
});
|
|
43612
43703
|
var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/opencodeRuntime") {};
|
|
43613
|
-
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$
|
|
43704
|
+
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$68));
|
|
43614
43705
|
//#endregion
|
|
43615
43706
|
//#region src/textGeneration/TextGenerationUtils.ts
|
|
43616
43707
|
const isTextGenerationError$2 = Schema$1.is(TextGenerationError);
|
|
@@ -82703,9 +82794,9 @@ const WorkspaceEntriesLayerLive = layer$26.pipe(Layer.provide(layer$42));
|
|
|
82703
82794
|
const WorkspaceFileSystemLayerLive = layer$25.pipe(Layer.provide(layer$42), Layer.provide(WorkspaceEntriesLayerLive));
|
|
82704
82795
|
const WorkspaceLayerLive = Layer.mergeAll(layer$42, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
|
|
82705
82796
|
const ProjectFaviconResolverLayerLive = layer$40.pipe(Layer.provide(layer$42), Layer.provide(layer$41));
|
|
82706
|
-
const AuthLayerLive = layer$
|
|
82797
|
+
const AuthLayerLive = layer$60.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$64));
|
|
82707
82798
|
const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
|
|
82708
|
-
const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$50), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$
|
|
82799
|
+
const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$50), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$64))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$51), Layer.provideMerge(layer$44), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$56), Layer.provideMerge(layer$64)).pipe(Layer.provideMerge(layer$8), Layer.provideMerge(layer$7), Layer.provideMerge(layer$6), Layer.provideMerge(layer$47), Layer.provideMerge(layer$49), Layer.provideMerge(layer$48), Layer.provide(layer$68));
|
|
82709
82800
|
/**
|
|
82710
82801
|
* Hub asset sync.
|
|
82711
82802
|
*
|
|
@@ -82773,6 +82864,151 @@ const serveCommand = Command.make("serve", { ...sharedServerCommandFlags }).pipe
|
|
|
82773
82864
|
forceAutoBootstrapProjectFromCwd: false
|
|
82774
82865
|
})));
|
|
82775
82866
|
//#endregion
|
|
82867
|
+
//#region src/service/cliSelfUpdate.ts
|
|
82868
|
+
/**
|
|
82869
|
+
* Upgrading the globally installed CLI, then the service that runs it.
|
|
82870
|
+
*
|
|
82871
|
+
* Two steps that have to happen in that order and from different binaries, and
|
|
82872
|
+
* getting the order wrong is silent: `service update` renders the unit from the
|
|
82873
|
+
* *running* CLI, and pins that version into the runtime directory. Run the
|
|
82874
|
+
* reconcile from the old process and you install a new CLI and then carefully
|
|
82875
|
+
* point the service back at the old one.
|
|
82876
|
+
*
|
|
82877
|
+
* So the second step re-executes the newly installed binary. That is also why
|
|
82878
|
+
* this is not `ServerSelfUpdate`: that path respawns the server in place, which
|
|
82879
|
+
* it explicitly refuses to do under launchd — KeepAlive would restart the
|
|
82880
|
+
* process being replaced and leave two servers on one port. Under a supervisor
|
|
82881
|
+
* the honest sequence is install, then let the supervisor restart, which is
|
|
82882
|
+
* exactly what `service update` does.
|
|
82883
|
+
*
|
|
82884
|
+
* @module service/cliSelfUpdate
|
|
82885
|
+
*/
|
|
82886
|
+
/** npm can take minutes when a native dependency compiles from source. */
|
|
82887
|
+
const CLI_INSTALL_TIMEOUT_MS = 600 * 1e3;
|
|
82888
|
+
const PACKAGE_NAME = "@p4code/cli";
|
|
82889
|
+
/**
|
|
82890
|
+
* Exact versions or the two dist-tags, and nothing else.
|
|
82891
|
+
*
|
|
82892
|
+
* The value is handed to npm and printed back as "installed X". An arbitrary
|
|
82893
|
+
* string could be a git URL or a local path, which npm would happily install
|
|
82894
|
+
* and which would make the acknowledgement a lie.
|
|
82895
|
+
*/
|
|
82896
|
+
const EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/u;
|
|
82897
|
+
const isInstallableVersion = (value) => value === "latest" || value === "next" || EXACT_VERSION_PATTERN.test(value);
|
|
82898
|
+
var CliSelfUpdateError = class extends Schema$1.TaggedErrorClass()("CliSelfUpdateError", {
|
|
82899
|
+
step: Schema$1.String,
|
|
82900
|
+
detail: Schema$1.optional(Schema$1.String)
|
|
82901
|
+
}) {
|
|
82902
|
+
get message() {
|
|
82903
|
+
return this.detail === void 0 ? `Self-update failed while ${this.step}.` : `Self-update failed while ${this.step}: ${this.detail}`;
|
|
82904
|
+
}
|
|
82905
|
+
};
|
|
82906
|
+
/**
|
|
82907
|
+
* Where `npm install --global` put the executable.
|
|
82908
|
+
*
|
|
82909
|
+
* Asked of npm rather than assumed, because the answer differs between a
|
|
82910
|
+
* Homebrew node, an nvm one and a system one — and the whole point of this step
|
|
82911
|
+
* is to run the binary that was just written, not the one on this process's
|
|
82912
|
+
* PATH, which a shell may have cached.
|
|
82913
|
+
*/
|
|
82914
|
+
const resolveGlobalBinary = Effect.fn("cli.selfUpdate.resolveGlobalBinary")(function* (binaryName) {
|
|
82915
|
+
const result = yield* (yield* ProcessRunner).run({
|
|
82916
|
+
command: "npm",
|
|
82917
|
+
args: ["prefix", "--global"]
|
|
82918
|
+
}).pipe(Effect.mapError((cause) => new CliSelfUpdateError({
|
|
82919
|
+
step: "locating the npm global prefix",
|
|
82920
|
+
detail: String(cause)
|
|
82921
|
+
})));
|
|
82922
|
+
if (result.code !== 0) return yield* new CliSelfUpdateError({
|
|
82923
|
+
step: "locating the npm global prefix",
|
|
82924
|
+
detail: `npm exited with code ${String(result.code)}`
|
|
82925
|
+
});
|
|
82926
|
+
const prefix = result.stdout.trim();
|
|
82927
|
+
if (prefix.length === 0) return yield* new CliSelfUpdateError({
|
|
82928
|
+
step: "locating the npm global prefix",
|
|
82929
|
+
detail: "npm printed no prefix"
|
|
82930
|
+
});
|
|
82931
|
+
return `${prefix}/bin/${binaryName}`;
|
|
82932
|
+
});
|
|
82933
|
+
const installGlobalCli = Effect.fn("cli.selfUpdate.installGlobalCli")(function* (input) {
|
|
82934
|
+
const runner = yield* ProcessRunner;
|
|
82935
|
+
const step = `installing ${PACKAGE_NAME}@${input.version}`;
|
|
82936
|
+
const result = yield* runner.run({
|
|
82937
|
+
command: "npm",
|
|
82938
|
+
args: [
|
|
82939
|
+
"install",
|
|
82940
|
+
"--global",
|
|
82941
|
+
"--no-fund",
|
|
82942
|
+
"--no-audit",
|
|
82943
|
+
`${PACKAGE_NAME}@${input.version}`
|
|
82944
|
+
],
|
|
82945
|
+
timeout: CLI_INSTALL_TIMEOUT_MS
|
|
82946
|
+
}).pipe(Effect.mapError((cause) => new CliSelfUpdateError({
|
|
82947
|
+
step,
|
|
82948
|
+
detail: String(cause)
|
|
82949
|
+
})));
|
|
82950
|
+
if (result.code !== 0) return yield* new CliSelfUpdateError({
|
|
82951
|
+
step,
|
|
82952
|
+
detail: result.stderr.trim().length > 0 ? result.stderr.trim() : `exit code ${String(result.code)}`
|
|
82953
|
+
});
|
|
82954
|
+
});
|
|
82955
|
+
/**
|
|
82956
|
+
* Ask the freshly installed binary what version it is.
|
|
82957
|
+
*
|
|
82958
|
+
* A completed npm install can still be unusable — a native dependency that
|
|
82959
|
+
* failed to build, a Node too old — and it is better to find that out here,
|
|
82960
|
+
* while the service is still running the version that works, than after
|
|
82961
|
+
* pointing the unit at it.
|
|
82962
|
+
*/
|
|
82963
|
+
const readInstalledVersion = Effect.fn("cli.selfUpdate.readInstalledVersion")(function* (binaryPath) {
|
|
82964
|
+
const runner = yield* ProcessRunner;
|
|
82965
|
+
const step = "verifying the installed CLI";
|
|
82966
|
+
const result = yield* runner.run({
|
|
82967
|
+
command: binaryPath,
|
|
82968
|
+
args: ["--version"]
|
|
82969
|
+
}).pipe(Effect.mapError((cause) => new CliSelfUpdateError({
|
|
82970
|
+
step,
|
|
82971
|
+
detail: String(cause)
|
|
82972
|
+
})));
|
|
82973
|
+
if (result.code !== 0) return yield* new CliSelfUpdateError({
|
|
82974
|
+
step,
|
|
82975
|
+
detail: `the installed CLI exited with code ${String(result.code)}`
|
|
82976
|
+
});
|
|
82977
|
+
const match = /(\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?)/u.exec(result.stdout);
|
|
82978
|
+
if (match?.[1] === void 0) return yield* new CliSelfUpdateError({
|
|
82979
|
+
step,
|
|
82980
|
+
detail: "the installed CLI did not report a version"
|
|
82981
|
+
});
|
|
82982
|
+
return match[1];
|
|
82983
|
+
});
|
|
82984
|
+
/**
|
|
82985
|
+
* Hand the rest of the work to the new binary.
|
|
82986
|
+
*
|
|
82987
|
+
* `service update` from *this* process would pin this process's version. The
|
|
82988
|
+
* new binary reconciles to itself, which is the only way the unit ends up
|
|
82989
|
+
* pointing at what was just installed.
|
|
82990
|
+
*/
|
|
82991
|
+
const reconcileWithNewBinary = Effect.fn("cli.selfUpdate.reconcileWithNewBinary")(function* (input) {
|
|
82992
|
+
const runner = yield* ProcessRunner;
|
|
82993
|
+
const step = "updating the service to the new version";
|
|
82994
|
+
const result = yield* runner.run({
|
|
82995
|
+
command: input.binaryPath,
|
|
82996
|
+
args: [
|
|
82997
|
+
"service",
|
|
82998
|
+
"update",
|
|
82999
|
+
...input.baseDir === void 0 ? [] : ["--base-dir", input.baseDir]
|
|
83000
|
+
],
|
|
83001
|
+
timeout: CLI_INSTALL_TIMEOUT_MS
|
|
83002
|
+
}).pipe(Effect.mapError((cause) => new CliSelfUpdateError({
|
|
83003
|
+
step,
|
|
83004
|
+
detail: String(cause)
|
|
83005
|
+
})));
|
|
83006
|
+
if (result.code !== 0) return yield* new CliSelfUpdateError({
|
|
83007
|
+
step,
|
|
83008
|
+
detail: result.stderr.trim().length > 0 ? result.stderr.trim() : `exit code ${String(result.code)}`
|
|
83009
|
+
});
|
|
83010
|
+
});
|
|
83011
|
+
//#endregion
|
|
82776
83012
|
//#region src/cli/service.ts
|
|
82777
83013
|
const isBootServiceCommandError = Schema$1.is(BootServiceCommandError);
|
|
82778
83014
|
const bootServiceLayer = (config) => layer$46({
|
|
@@ -82918,6 +83154,39 @@ const serviceRestartCommand = Command.make("restart", projectLocationFlags).pipe
|
|
|
82918
83154
|
yield* service.restart;
|
|
82919
83155
|
yield* Console.log("Restarted the P4Code service.");
|
|
82920
83156
|
}))));
|
|
83157
|
+
/**
|
|
83158
|
+
* Install a newer CLI, then point the service at it.
|
|
83159
|
+
*
|
|
83160
|
+
* Deliberately not `ServerSelfUpdate`, which the server uses to replace itself
|
|
83161
|
+
* in place: that path refuses to run under launchd, because KeepAlive would
|
|
83162
|
+
* restart the process being replaced and leave two servers on one port. Under a
|
|
83163
|
+
* supervisor the honest sequence is install, then reconcile and let the
|
|
83164
|
+
* supervisor restart — which is what this does, from the new binary.
|
|
83165
|
+
*/
|
|
83166
|
+
const serviceSelfUpdateCommand = Command.make("self-update", {
|
|
83167
|
+
...projectLocationFlags,
|
|
83168
|
+
version: Flag.string("version").pipe(Flag.withDescription("Version or dist-tag to install (default latest)."), Flag.optional)
|
|
83169
|
+
}).pipe(Command.withDescription("Install the newest @p4code/cli and update the service to run it."), Command.withHandler((flags) => runServiceCommand(flags, Effect.gen(function* () {
|
|
83170
|
+
const requested = Option.getOrElse(flags.version, () => "latest");
|
|
83171
|
+
if (!isInstallableVersion(requested)) {
|
|
83172
|
+
yield* Console.error(`'${requested}' is not a version or dist-tag. Use an exact version, or latest.`);
|
|
83173
|
+
return;
|
|
83174
|
+
}
|
|
83175
|
+
yield* Console.log(`Installing ${PACKAGE_NAME}@${requested}…`);
|
|
83176
|
+
yield* installGlobalCli({ version: requested });
|
|
83177
|
+
const binaryPath = yield* resolveGlobalBinary("p4c");
|
|
83178
|
+
const installed = yield* readInstalledVersion(binaryPath);
|
|
83179
|
+
if (installed === version) {
|
|
83180
|
+
yield* Console.log(`Already running @p4code/cli@${installed}; nothing to update.`);
|
|
83181
|
+
return;
|
|
83182
|
+
}
|
|
83183
|
+
yield* Console.log(`Installed @p4code/cli@${installed}; updating the service…`);
|
|
83184
|
+
yield* reconcileWithNewBinary({
|
|
83185
|
+
binaryPath,
|
|
83186
|
+
baseDir: void 0
|
|
83187
|
+
});
|
|
83188
|
+
yield* Console.log(`Service now runs @p4code/cli@${installed}.`);
|
|
83189
|
+
}))));
|
|
82921
83190
|
const serviceStatusCommand = Command.make("status", projectLocationFlags).pipe(Command.withDescription("Show whether the P4Code background service is installed."), Command.withHandler((flags) => runServiceCommand(flags, Effect.gen(function* () {
|
|
82922
83191
|
const service = yield* BootService;
|
|
82923
83192
|
yield* Console.log(formatServiceStatus(yield* service.status, version));
|
|
@@ -82942,11 +83211,12 @@ const serviceCommand = Command.make("service").pipe(Command.withDescription("Man
|
|
|
82942
83211
|
serviceUninstallCommand,
|
|
82943
83212
|
serviceUpdateCommand,
|
|
82944
83213
|
serviceRestartCommand,
|
|
83214
|
+
serviceSelfUpdateCommand,
|
|
82945
83215
|
serviceStatusCommand
|
|
82946
83216
|
]));
|
|
82947
83217
|
//#endregion
|
|
82948
83218
|
//#region src/bin.ts
|
|
82949
|
-
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$
|
|
83219
|
+
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$68, FetchHttpClient.layer);
|
|
82950
83220
|
/**
|
|
82951
83221
|
* Prints an update reminder before dispatching, at most once a day.
|
|
82952
83222
|
*
|