@happyvertical/smrt-cli 0.53.4 → 0.53.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/README.md +24 -0
- package/agents/app-commands.md +28 -3
- package/dist/{ApiKey-BZmbOTnL-DPPF6Swn.js → ApiKey-BZmbOTnL-BbZupUZ3.js} +2 -2
- package/dist/ApiKeyCollection-Bfb_nw81-DYAZwx0O.js +87 -0
- package/dist/AuditLogCollection-B1YonCAP-Bkj2Ezon.js +193 -0
- package/dist/{NostrIdentity-n70E0vck-D60BSg6H.js → NostrIdentity-n70E0vck-Di3_eNCq.js} +4 -4
- package/dist/NostrIdentityCollection-Ca74dkaL-DvjOg9Xy.js +127 -0
- package/dist/{OidcIdentity-Cb1bxreM-B2eV2ahq.js → OidcIdentity-Cb1bxreM-LXmC7uAf.js} +2 -2
- package/dist/OidcIdentityCollection-TV8VdK69-Cv1ZTeuk.js +86 -0
- package/dist/ProfileAssetCollection-B9hacUJa-CPuaWLk4.js +101 -0
- package/dist/{ProfileCollection-DuJT7Mum-B3Uhqphs.js → ProfileCollection-DuJT7Mum-nXbHrPpX.js} +31 -31
- package/dist/ProfileLinkCollection-4uNURz32-CbKZYmYm.js +233 -0
- package/dist/ProfileMetadataCollection-DQhbfEfa-_1rQCLTR.js +108 -0
- package/dist/ProfileMetafieldCollection-CoBuM_5G-CPjW3l5r.js +145 -0
- package/dist/ProfileRelationshipCollection-DUVgaIqf-RCDqYz6O.js +161 -0
- package/dist/ProfileRelationshipTermCollection-DtA9OM23-BoGhQBSQ.js +107 -0
- package/dist/{ProfileRelationshipType-BRiRka6t-Ci-qvFhm.js → ProfileRelationshipType-BRiRka6t-Dezjv5jP.js} +1 -1
- package/dist/ProfileRelationshipTypeCollection-BdXwd7Kf-HoTmV1q-.js +57 -0
- package/dist/app/cli.d.ts +7 -0
- package/dist/app/cli.d.ts.map +1 -1
- package/dist/app/errors.d.ts +34 -1
- package/dist/app/errors.d.ts.map +1 -1
- package/dist/app/index.d.ts +4 -3
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/launchers.d.ts +11 -0
- package/dist/app/launchers.d.ts.map +1 -1
- package/dist/app/operations.d.ts +2 -0
- package/dist/app/operations.d.ts.map +1 -1
- package/dist/app/runtime.d.ts +17 -2
- package/dist/app/runtime.d.ts.map +1 -1
- package/dist/app/tokens.d.ts +21 -0
- package/dist/app/tokens.d.ts.map +1 -0
- package/dist/app.js +3 -3
- package/dist/{cli-BRFsobey.js → cli-OZsH5cdL.js} +403 -76
- package/dist/cli-generator.d.ts.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/utilities.d.ts.map +1 -1
- package/dist/{commands-dFz1e9A6.js → commands-UXK2F_0j.js} +35 -51
- package/dist/database-environment-D_Gk1Brf.js +20 -0
- package/dist/database-environment.d.ts +43 -0
- package/dist/database-environment.d.ts.map +1 -0
- package/dist/dist-BVjJWjT-.js +20 -0
- package/dist/{dist-EJ63xUEp.js → dist-C87Ib9Vx.js} +2 -2
- package/dist/{dist-C2BqYVu7.js → dist-DLw_E34G.js} +1654 -1748
- package/dist/{dist-Berwe80z.js → dist-GIaWaUho.js} +4 -4
- package/dist/{dist-D4BNILLN.js → dist-RSE5hCY4.js} +3 -3
- package/dist/index.js +15 -10
- package/dist/{oidcProvisioningPrimitives-D9OYu5-F-B-kYsBTM.js → oidcProvisioningPrimitives-D9OYu5-F-T9KkkDCd.js} +5 -5
- package/dist/{portability-DmQWSSXA.js → portability-CES1YC2m.js} +1 -1
- package/dist/resolveIdentity-B-PK6RwU-C9ogf8YG.js +269 -0
- package/package.json +15 -15
- package/dist/ApiKeyCollection-Bfb_nw81-B1-2YKj8.js +0 -2
- package/dist/AuditLogCollection-B1YonCAP-DP4PCCw-.js +0 -2
- package/dist/NostrIdentityCollection-Ca74dkaL-C-v57o9N.js +0 -2
- package/dist/OidcIdentityCollection-TV8VdK69-BodvOtPJ.js +0 -2
- package/dist/ProfileAssetCollection-B9hacUJa-B7PLe0ld.js +0 -2
- package/dist/ProfileLinkCollection-4uNURz32-CLY1cBnn.js +0 -2
- package/dist/ProfileMetadataCollection-DQhbfEfa-Bzv-E4ep.js +0 -2
- package/dist/ProfileMetafieldCollection-CoBuM_5G-D-VqQuY1.js +0 -2
- package/dist/ProfileRelationshipCollection-DUVgaIqf-gEkDBmkh.js +0 -2
- package/dist/ProfileRelationshipTermCollection-DtA9OM23-DI_awdol.js +0 -2
- package/dist/ProfileRelationshipTypeCollection-BdXwd7Kf-CaQMLH9f.js +0 -2
- package/dist/resolveIdentity-B-PK6RwU-BmAU1Kse.js +0 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { accessSync, constants, cpSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
2
|
+
import { accessSync, chmodSync, closeSync, constants, cpSync, existsSync, fstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
import { acquireWriterLease, createProviderReadinessProbe, findInstalledPackage, prepareApplicationStateRoot, readActiveWriterLease, resolveApplicationId, resolveApplicationStateRoot, runtimeConfigurationFingerprint, withOperationLock } from "@happyvertical/smrt-app-runtime";
|
|
5
|
+
import { acquireWriterLease, createProviderReadinessProbe, findInstalledPackage, openLocalMcpTokenStore, prepareApplicationStateRoot, readActiveWriterLease, resolveApplicationId, resolveApplicationStateRoot, runtimeConfigurationFingerprint, withOperationLock } from "@happyvertical/smrt-app-runtime";
|
|
6
6
|
import { spawn, spawnSync } from "node:child_process";
|
|
7
7
|
import { platform } from "node:os";
|
|
8
8
|
import { randomBytes } from "node:crypto";
|
|
@@ -27,10 +27,81 @@ var AppCommandError = class extends Error {
|
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* A `smrt app start` whose web process never proved readiness. Carries the
|
|
32
|
+
* already-redacted, bounded tail of that process's output and the private
|
|
33
|
+
* log it came from, for the error envelope.
|
|
34
|
+
*/
|
|
35
|
+
var ApplicationStartError = class extends Error {
|
|
36
|
+
output;
|
|
37
|
+
logFile;
|
|
38
|
+
constructor(message, output, logFile) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = "ApplicationStartError";
|
|
41
|
+
this.output = output;
|
|
42
|
+
this.logFile = logFile;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/** Replacement marker for every redacted value. */
|
|
46
|
+
var REDACTED = "[redacted]";
|
|
30
47
|
/** Minimum length before an environment value is treated as redactable. */
|
|
31
48
|
var MIN_REDACTED_VALUE_LENGTH = 8;
|
|
32
49
|
/** Environment names whose values must never reach operator output. */
|
|
33
50
|
var SECRET_ENVIRONMENT_NAME = /(?:^|_)(?:DATABASE_URL|URL|DSN|TOKEN|SECRET|PASSWORD|PASSWD|KEY|CREDENTIALS?|AUTH)(?:_|$)/i;
|
|
51
|
+
/** URL userinfo (`scheme://user:pass@`); group 2 is the credential span. */
|
|
52
|
+
var URL_USERINFO = /\b([a-z][a-z0-9+.-]*:\/\/)([^\s/@:'"]*(?::[^\s/@'"]*)?)@/dgi;
|
|
53
|
+
/** Query/parameter credential (`?token=…`); group 2 is the value span. */
|
|
54
|
+
var QUERY_CREDENTIAL = /([?&;](?:token|password|secret|key|access_token)=)([^&\s"'<>]+)/dgi;
|
|
55
|
+
/** Bearer token; group 2 is the token span (8+ characters unless strict). */
|
|
56
|
+
var BEARER = /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{8,})/dg;
|
|
57
|
+
var BEARER_ANY_LENGTH = /\b(Bearer\s+)([A-Za-z0-9._~+/=-]+)/dg;
|
|
58
|
+
function structuralSpans(text, strict) {
|
|
59
|
+
const spans = [];
|
|
60
|
+
for (const pattern of [
|
|
61
|
+
URL_USERINFO,
|
|
62
|
+
QUERY_CREDENTIAL,
|
|
63
|
+
strict ? BEARER_ANY_LENGTH : BEARER
|
|
64
|
+
]) for (const match of text.matchAll(pattern)) {
|
|
65
|
+
const span = match.indices?.[2];
|
|
66
|
+
if (span && (span[1] > span[0] || pattern === URL_USERINFO)) spans.push([span[0], span[1]]);
|
|
67
|
+
}
|
|
68
|
+
return spans;
|
|
69
|
+
}
|
|
70
|
+
/** Every existing `[redacted]` marker in `text`: opaque to later passes. */
|
|
71
|
+
function markerSpans(text) {
|
|
72
|
+
return literalSpans(text, [REDACTED], []);
|
|
73
|
+
}
|
|
74
|
+
function literalSpans(text, values, opaque) {
|
|
75
|
+
const spans = [];
|
|
76
|
+
for (const value of values) for (let index = text.indexOf(value); index !== -1; index = text.indexOf(value, index + 1)) {
|
|
77
|
+
const end = index + value.length;
|
|
78
|
+
if (opaque.some(([start, stop]) => index >= start && end <= stop)) continue;
|
|
79
|
+
spans.push([index, end]);
|
|
80
|
+
}
|
|
81
|
+
return spans;
|
|
82
|
+
}
|
|
83
|
+
/** Replace the union of `spans` (overlapping or touching spans merge). */
|
|
84
|
+
function maskSpans(text, spans) {
|
|
85
|
+
if (spans.length === 0) return text;
|
|
86
|
+
spans.sort((left, right) => left[0] - right[0] || right[1] - left[1]);
|
|
87
|
+
let result = "";
|
|
88
|
+
let cursor = 0;
|
|
89
|
+
let [start, end] = spans[0];
|
|
90
|
+
const flush = () => {
|
|
91
|
+
result += text.slice(cursor, start) + REDACTED;
|
|
92
|
+
cursor = end;
|
|
93
|
+
};
|
|
94
|
+
for (const [nextStart, nextEnd] of spans.slice(1)) {
|
|
95
|
+
if (nextStart <= end) {
|
|
96
|
+
end = Math.max(end, nextEnd);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
flush();
|
|
100
|
+
[start, end] = [nextStart, nextEnd];
|
|
101
|
+
}
|
|
102
|
+
flush();
|
|
103
|
+
return result + text.slice(cursor);
|
|
104
|
+
}
|
|
34
105
|
/**
|
|
35
106
|
* Remove secret material from an operator-facing message.
|
|
36
107
|
*
|
|
@@ -38,12 +109,37 @@ var SECRET_ENVIRONMENT_NAME = /(?:^|_)(?:DATABASE_URL|URL|DSN|TOKEN|SECRET|PASSW
|
|
|
38
109
|
* connection string, credential, or bootstrap token. Redaction covers:
|
|
39
110
|
* literal values of secret-named environment variables, URL userinfo,
|
|
40
111
|
* `token=`/`password=`/`secret=` query values, and bearer tokens.
|
|
112
|
+
*
|
|
113
|
+
* Every span is located on the raw text and the union is masked at once, so
|
|
114
|
+
* a literal value can never split a structural match (an env value that is
|
|
115
|
+
* a Bearer-token prefix, or a URL's scheme and host, used to leave the rest
|
|
116
|
+
* of the token or the URL password visible). A final structural pass over
|
|
117
|
+
* the masked text keeps everything the earlier sequential order masked.
|
|
41
118
|
*/
|
|
42
|
-
function redactSecrets(message, environment = process.env) {
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
119
|
+
function redactSecrets(message, environment = process.env, options = {}) {
|
|
120
|
+
const strict = options.strict === true;
|
|
121
|
+
const minimumLength = strict ? 1 : MIN_REDACTED_VALUE_LENGTH;
|
|
122
|
+
const secretValues = Object.entries(environment).filter(([name, value]) => typeof value === "string" && value.length >= minimumLength && SECRET_ENVIRONMENT_NAME.test(name)).map(([, value]) => value);
|
|
123
|
+
const markers = markerSpans(message);
|
|
124
|
+
const masked = maskSpans(message, [
|
|
125
|
+
...markers,
|
|
126
|
+
...structuralSpans(message, strict),
|
|
127
|
+
...literalSpans(message, secretValues, markers)
|
|
128
|
+
]);
|
|
129
|
+
return maskSpans(masked, [...markerSpans(masked), ...structuralSpans(masked, strict)]);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The last `maxBytes` of `text`: a leading partial line (and any split
|
|
133
|
+
* multi-byte character) is dropped when the cut lands mid-text. Redact
|
|
134
|
+
* before cutting; cutting never exposes anything redaction masked.
|
|
135
|
+
*/
|
|
136
|
+
function boundedTail(text, maxBytes) {
|
|
137
|
+
const bytes = Buffer.from(text, "utf8");
|
|
138
|
+
if (bytes.length <= maxBytes) return text;
|
|
139
|
+
const tail = bytes.subarray(bytes.length - maxBytes).toString("utf8");
|
|
140
|
+
let cut = tail.slice(tail.indexOf("\n") + 1);
|
|
141
|
+
while (Buffer.byteLength(cut) > maxBytes) cut = cut.slice(1);
|
|
142
|
+
return cut;
|
|
47
143
|
}
|
|
48
144
|
//#endregion
|
|
49
145
|
//#region src/app/identity.ts
|
|
@@ -162,14 +258,22 @@ function createBrowserOpener(sourceRoot) {
|
|
|
162
258
|
runChild(sourceRoot, binary, args);
|
|
163
259
|
};
|
|
164
260
|
}
|
|
165
|
-
/**
|
|
261
|
+
/**
|
|
262
|
+
* Default runtime resolver: reload `smrt.config` without the cache.
|
|
263
|
+
*
|
|
264
|
+
* Profile selection is `resolveEffectiveApplicationRuntime()` from
|
|
265
|
+
* smrt-config — the rule the web process applies in app-runtime's SvelteKit
|
|
266
|
+
* entry — so the operator and the server it manages never disagree: no
|
|
267
|
+
* `runtime` block (or no config file) is the `local` profile, and a present
|
|
268
|
+
* value that is not a valid block (`null`, `false`, `0`, `''`, ...) fails
|
|
269
|
+
* closed.
|
|
270
|
+
*/
|
|
166
271
|
async function resolveConfiguredRuntime(sourceRoot) {
|
|
167
|
-
const { loadConfig,
|
|
168
|
-
await loadConfig({
|
|
272
|
+
const { loadConfig, resolveEffectiveApplicationRuntime } = await import("@happyvertical/smrt-config");
|
|
273
|
+
return resolveEffectiveApplicationRuntime(await loadConfig({
|
|
169
274
|
cache: false,
|
|
170
275
|
searchFrom: sourceRoot
|
|
171
|
-
});
|
|
172
|
-
return resolveConfiguredApplicationRuntime();
|
|
276
|
+
}));
|
|
173
277
|
}
|
|
174
278
|
/** Build the default dependency set for `sourceRoot`. */
|
|
175
279
|
async function defaultDependencies(sourceRoot) {
|
|
@@ -391,6 +495,23 @@ function resolveViteEntry(sourceRoot) {
|
|
|
391
495
|
if (!existsSync(entry)) throw new ViteLauncherError("The installed Vite package CLI is missing; reinstall dependencies.");
|
|
392
496
|
return entry;
|
|
393
497
|
}
|
|
498
|
+
/** Loopback host every `smrt app` URL (onboarding, `open`, `start`) names. */
|
|
499
|
+
var LOOPBACK_HOST = "127.0.0.1";
|
|
500
|
+
/**
|
|
501
|
+
* Arguments for `smrt app dev`: the caller's Vite arguments, plus
|
|
502
|
+
* `--host 127.0.0.1` unless they name `--host`, and `--port $PORT` when
|
|
503
|
+
* `PORT` is set and they name no `--port`. Vite's own default host is
|
|
504
|
+
* `localhost`, which Node resolves to `[::1]` first, so the dev server would
|
|
505
|
+
* not answer the `http://127.0.0.1:<PORT>` URL onboarding and `app:open` hand
|
|
506
|
+
* out. An explicit `--host`/`--port` (or `smrt app vite dev …`) wins.
|
|
507
|
+
*/
|
|
508
|
+
function devServerArguments(args, env = process.env) {
|
|
509
|
+
const names = (flag) => args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
|
|
510
|
+
const result = [...args];
|
|
511
|
+
if (!names("--host")) result.push("--host", LOOPBACK_HOST);
|
|
512
|
+
if (!names("--port") && env.PORT) result.push("--port", env.PORT);
|
|
513
|
+
return result;
|
|
514
|
+
}
|
|
394
515
|
/**
|
|
395
516
|
* Run the application's Vite CLI in this process with `args` verbatim. The
|
|
396
517
|
* caller has already loaded `.env` without overriding the shell.
|
|
@@ -576,9 +697,14 @@ var APP_OPERATIONS = [
|
|
|
576
697
|
"export",
|
|
577
698
|
"import"
|
|
578
699
|
];
|
|
579
|
-
function printJson(context, value, pretty = false) {
|
|
700
|
+
function printJson$1(context, value, pretty = false) {
|
|
580
701
|
context.io.stdout(`${pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value)}\n`);
|
|
581
702
|
}
|
|
703
|
+
/** Print the one-time URL to the operator's terminal only, never to logs. */
|
|
704
|
+
function printOnboardingUrl(context, url) {
|
|
705
|
+
if (!url || !context.io.operatorTerminal) return;
|
|
706
|
+
context.io.operatorTerminal(`Open this one-time owner setup link on this device (it works once and expires):\n ${url}\n`);
|
|
707
|
+
}
|
|
582
708
|
function ensurePrivateDirectory(path) {
|
|
583
709
|
mkdirSync(path, {
|
|
584
710
|
recursive: true,
|
|
@@ -710,7 +836,7 @@ async function setup(context, operationLock) {
|
|
|
710
836
|
onboardingRecovery: onboardingAvailable ? "pnpm app:open" : null,
|
|
711
837
|
secretValuesIncluded: false
|
|
712
838
|
};
|
|
713
|
-
printJson(context, report, true);
|
|
839
|
+
printJson$1(context, report, true);
|
|
714
840
|
return {
|
|
715
841
|
...report,
|
|
716
842
|
onboardingUrl: retainedOnboardingUrl
|
|
@@ -728,15 +854,17 @@ async function recoverOnboarding(context, operationLock) {
|
|
|
728
854
|
const initialized = await initializeLocal(context, runtime, env);
|
|
729
855
|
if (!initialized) throw new Error("Local runtime initialization failed.");
|
|
730
856
|
const invitation = await initialized.runtime.rotateBootstrapInvitation();
|
|
731
|
-
|
|
857
|
+
const url = `http://127.0.0.1:${env.PORT || "5173"}/setup?token=${encodeURIComponent(invitation.token)}`;
|
|
858
|
+
saveOnboardingUrl(context, url);
|
|
732
859
|
await initialized.runtime.db.close?.();
|
|
733
|
-
printJson(context, {
|
|
860
|
+
printJson$1(context, {
|
|
734
861
|
schemaVersion: 1,
|
|
735
862
|
status: "ready",
|
|
736
863
|
onboardingAvailable: true,
|
|
737
864
|
recovery: "Run pnpm app:start, then pnpm app:open.",
|
|
738
865
|
secretValuesIncluded: false
|
|
739
866
|
});
|
|
867
|
+
printOnboardingUrl(context, url);
|
|
740
868
|
} finally {
|
|
741
869
|
operatorLease.release();
|
|
742
870
|
}
|
|
@@ -778,7 +906,7 @@ async function start(context, operationLock) {
|
|
|
778
906
|
const existing = readOwnedProcess(pidPath(context));
|
|
779
907
|
if (existing) {
|
|
780
908
|
await waitForReady(context, url, existing.pid, existing.instance, configuration);
|
|
781
|
-
printJson(context, {
|
|
909
|
+
printJson$1(context, {
|
|
782
910
|
schemaVersion: 1,
|
|
783
911
|
status: "running",
|
|
784
912
|
pid: existing.pid
|
|
@@ -787,18 +915,30 @@ async function start(context, operationLock) {
|
|
|
787
915
|
}
|
|
788
916
|
ensurePrivateDirectory(preparedStateRoot(context));
|
|
789
917
|
const instance = randomBytes(16).toString("hex");
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
918
|
+
const childEnv = {
|
|
919
|
+
...env,
|
|
920
|
+
HOST: runtime.profile === "local" ? "127.0.0.1" : env.HOST || "0.0.0.0",
|
|
921
|
+
PORT: env.PORT || "5173",
|
|
922
|
+
SMRT_PROCESS_INSTANCE: instance,
|
|
923
|
+
SMRT_OPERATION_INSTANCE: operationLock?.instance
|
|
924
|
+
};
|
|
925
|
+
const logFile = startLogPath(context);
|
|
926
|
+
const logFd = openStartLog(logFile);
|
|
927
|
+
let child;
|
|
928
|
+
try {
|
|
929
|
+
child = spawn(process.execPath, [webLauncherPath(), `--smrt-instance=${instance}`], {
|
|
930
|
+
cwd: context.sourceRoot,
|
|
931
|
+
env: childEnv,
|
|
932
|
+
detached: true,
|
|
933
|
+
stdio: [
|
|
934
|
+
"ignore",
|
|
935
|
+
logFd,
|
|
936
|
+
logFd
|
|
937
|
+
]
|
|
938
|
+
});
|
|
939
|
+
} finally {
|
|
940
|
+
closeSync(logFd);
|
|
941
|
+
}
|
|
802
942
|
child.unref();
|
|
803
943
|
const pid = child.pid;
|
|
804
944
|
if (pid === void 0) throw new Error("The application process could not be started.");
|
|
@@ -811,15 +951,60 @@ async function start(context, operationLock) {
|
|
|
811
951
|
} catch (error) {
|
|
812
952
|
if (!await terminateFailedStart(context, child)) throw new Error(`${error instanceof Error ? error.message : "The application did not become ready."} Application process ${pid} did not exit after SIGTERM and SIGKILL and remains recorded; run pnpm app:stop.`);
|
|
813
953
|
rmSync(pidPath(context), { force: true });
|
|
814
|
-
throw error;
|
|
954
|
+
throw new ApplicationStartError(error instanceof Error ? error.message : "The application did not become ready.", redactedLogTail(logFile, childEnv), logFile);
|
|
815
955
|
}
|
|
816
|
-
printJson(context, {
|
|
956
|
+
printJson$1(context, {
|
|
817
957
|
schemaVersion: 1,
|
|
818
958
|
status: "started",
|
|
819
959
|
pid
|
|
820
960
|
});
|
|
821
961
|
return pid;
|
|
822
962
|
}
|
|
963
|
+
/** Upper bound on the web-process output a failed `start` reports. */
|
|
964
|
+
var START_OUTPUT_TAIL_BYTES = 8 * 1024;
|
|
965
|
+
/** Window read before redaction, so a cut never splits a reported secret. */
|
|
966
|
+
var START_OUTPUT_WINDOW_BYTES = 64 * 1024;
|
|
967
|
+
function startLogPath(context) {
|
|
968
|
+
return join(preparedStateRoot(context), "app.log");
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Create a fresh 0600 `app.log` for this start. The previous run's log is
|
|
972
|
+
* removed first and the new one is created exclusively, so a planted link or
|
|
973
|
+
* file is never written through.
|
|
974
|
+
*/
|
|
975
|
+
function openStartLog(path) {
|
|
976
|
+
rmSync(path, { force: true });
|
|
977
|
+
return openSync(path, "wx", 384);
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* The last {@link START_OUTPUT_TAIL_BYTES} of `path`, strictly redacted
|
|
981
|
+
* against the child's environment. A bounded window is redacted before it is cut, and a
|
|
982
|
+
* window that starts mid-file drops its leading partial record (all of it
|
|
983
|
+
* when the window holds no newline), so no fragment of a secret whose
|
|
984
|
+
* prefix lies before the window can survive the cut.
|
|
985
|
+
*/
|
|
986
|
+
function redactedLogTail(path, env) {
|
|
987
|
+
let text;
|
|
988
|
+
try {
|
|
989
|
+
const fd = openSync(path, "r");
|
|
990
|
+
try {
|
|
991
|
+
const size = fstatSync(fd).size;
|
|
992
|
+
const length = Math.min(size, START_OUTPUT_WINDOW_BYTES);
|
|
993
|
+
const buffer = Buffer.alloc(length);
|
|
994
|
+
readSync(fd, buffer, 0, length, size - length);
|
|
995
|
+
text = buffer.toString("utf8");
|
|
996
|
+
if (length < size) {
|
|
997
|
+
const newline = text.indexOf("\n");
|
|
998
|
+
text = newline === -1 ? "" : text.slice(newline + 1);
|
|
999
|
+
}
|
|
1000
|
+
} finally {
|
|
1001
|
+
closeSync(fd);
|
|
1002
|
+
}
|
|
1003
|
+
} catch {
|
|
1004
|
+
return "";
|
|
1005
|
+
}
|
|
1006
|
+
return boundedTail(redactSecrets(text, env, { strict: true }), START_OUTPUT_TAIL_BYTES);
|
|
1007
|
+
}
|
|
823
1008
|
/**
|
|
824
1009
|
* Terminate a launcher that failed to prove readiness: SIGTERM, wait, then
|
|
825
1010
|
* SIGKILL, wait. Resolves `true` only once the child has provably exited.
|
|
@@ -847,7 +1032,7 @@ async function stop(context) {
|
|
|
847
1032
|
const pid = record?.pid || null;
|
|
848
1033
|
if (!record || !pid) {
|
|
849
1034
|
rmSync(pidPath(context), { force: true });
|
|
850
|
-
printJson(context, {
|
|
1035
|
+
printJson$1(context, {
|
|
851
1036
|
schemaVersion: 1,
|
|
852
1037
|
status: "stopped"
|
|
853
1038
|
});
|
|
@@ -857,7 +1042,7 @@ async function stop(context) {
|
|
|
857
1042
|
if (state === "unverifiable") throw new Error(`Application process ${pid} is live but its identity could not be re-verified; app.pid was kept and no signal was sent. Retry pnpm app:stop.`);
|
|
858
1043
|
if (state !== "owned" || !sendTerminationSignal(pid)) {
|
|
859
1044
|
rmSync(pidPath(context), { force: true });
|
|
860
|
-
printJson(context, {
|
|
1045
|
+
printJson$1(context, {
|
|
861
1046
|
schemaVersion: 1,
|
|
862
1047
|
status: "stopped",
|
|
863
1048
|
pid
|
|
@@ -874,7 +1059,7 @@ async function stop(context) {
|
|
|
874
1059
|
if (attempt === 39) throw new Error(`Application process ${pid} did not stop cleanly.`);
|
|
875
1060
|
}
|
|
876
1061
|
rmSync(pidPath(context), { force: true });
|
|
877
|
-
printJson(context, {
|
|
1062
|
+
printJson$1(context, {
|
|
878
1063
|
schemaVersion: 1,
|
|
879
1064
|
status: "stopped",
|
|
880
1065
|
pid
|
|
@@ -887,7 +1072,7 @@ async function open(context) {
|
|
|
887
1072
|
if (onboardingUrl) saveOnboardingLaunch(context, onboardingUrl);
|
|
888
1073
|
const destination = onboardingUrl ? pathToFileURL(onboardingLaunchPath(context)).toString() : url;
|
|
889
1074
|
context.deps.openBrowser(destination);
|
|
890
|
-
printJson(context, {
|
|
1075
|
+
printJson$1(context, {
|
|
891
1076
|
schemaVersion: 1,
|
|
892
1077
|
status: "opened",
|
|
893
1078
|
url
|
|
@@ -1009,10 +1194,10 @@ async function doctor(context) {
|
|
|
1009
1194
|
findings,
|
|
1010
1195
|
secretValuesIncluded: false
|
|
1011
1196
|
};
|
|
1012
|
-
printJson(context, report, true);
|
|
1197
|
+
printJson$1(context, report, true);
|
|
1013
1198
|
return report.status === "error" ? 1 : 0;
|
|
1014
1199
|
}
|
|
1015
|
-
async function backup(context, args
|
|
1200
|
+
async function backup(context, args) {
|
|
1016
1201
|
if ((await context.deps.resolveRuntime(context.sourceRoot)).profile !== "local") throw new Error("This scaffold delegates deployed backups to the selected operator or managed provider.");
|
|
1017
1202
|
const explicitDestination = args[0] ? assertExternalArtifactPath({
|
|
1018
1203
|
sourceRoot: context.sourceRoot,
|
|
@@ -1024,41 +1209,42 @@ async function backup(context, args, operationLock) {
|
|
|
1024
1209
|
dataDirectory: process.env.SMRT_DATA_DIR,
|
|
1025
1210
|
sourceRoot: context.sourceRoot
|
|
1026
1211
|
});
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
const destination = assertExternalArtifactPath({
|
|
1030
|
-
sourceRoot: context.sourceRoot,
|
|
1031
|
-
path: explicitDestination || join(dirname(paths.root), "backups", `${context.appId}-${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-")}`),
|
|
1032
|
-
label: "Backup destination"
|
|
1033
|
-
});
|
|
1034
|
-
ensurePrivateDirectory(dirname(destination));
|
|
1035
|
-
try {
|
|
1036
|
-
mkdirSync(destination, { mode: 448 });
|
|
1037
|
-
} catch (error) {
|
|
1038
|
-
if (errorCode(error) === "EEXIST") throw new Error(`Backup destination already exists: ${destination}`);
|
|
1039
|
-
throw error;
|
|
1040
|
-
}
|
|
1212
|
+
await withOperationLock(preparedStateRoot(context), "backup", async (lock) => {
|
|
1213
|
+
const operatorLease = acquireWriterLease(preparedStateRoot(context), { operationInstance: lock.instance });
|
|
1041
1214
|
try {
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1215
|
+
const destination = assertExternalArtifactPath({
|
|
1216
|
+
sourceRoot: context.sourceRoot,
|
|
1217
|
+
path: explicitDestination || join(dirname(paths.root), "backups", `${context.appId}-${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-")}`),
|
|
1218
|
+
label: "Backup destination"
|
|
1046
1219
|
});
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1220
|
+
const storageRelative = relative(paths.root, destination);
|
|
1221
|
+
if (storageRelative === "" || !isAbsolute(storageRelative) && storageRelative !== ".." && !storageRelative.startsWith(`..${sep}`)) throw new Error("Backup destination must remain outside local storage.");
|
|
1222
|
+
ensurePrivateDirectory(dirname(destination));
|
|
1223
|
+
try {
|
|
1224
|
+
mkdirSync(destination, { mode: 448 });
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
if (error.code !== "EEXIST") throw error;
|
|
1227
|
+
throw new Error(`Backup destination already exists: ${destination}`);
|
|
1228
|
+
}
|
|
1229
|
+
try {
|
|
1230
|
+
for (const entry of readdirSync(paths.root)) cpSync(join(paths.root, entry), join(destination, entry), {
|
|
1231
|
+
recursive: true,
|
|
1232
|
+
force: false,
|
|
1233
|
+
errorOnExist: true
|
|
1234
|
+
});
|
|
1235
|
+
chmodSync(destination, 448);
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
throw new Error(`Backup copy failed; destination preserved at ${destination}`, { cause: error });
|
|
1238
|
+
}
|
|
1239
|
+
printJson$1(context, {
|
|
1240
|
+
schemaVersion: 1,
|
|
1241
|
+
status: "backed-up",
|
|
1242
|
+
destination
|
|
1051
1243
|
});
|
|
1052
|
-
|
|
1244
|
+
} finally {
|
|
1245
|
+
operatorLease.release();
|
|
1053
1246
|
}
|
|
1054
|
-
|
|
1055
|
-
schemaVersion: 1,
|
|
1056
|
-
status: "backed-up",
|
|
1057
|
-
destination
|
|
1058
|
-
});
|
|
1059
|
-
} finally {
|
|
1060
|
-
operatorLease.release();
|
|
1061
|
-
}
|
|
1247
|
+
});
|
|
1062
1248
|
}
|
|
1063
1249
|
/**
|
|
1064
1250
|
* The application's own adapter at `scripts/smrt-portability.mjs` keeps
|
|
@@ -1068,7 +1254,7 @@ async function backup(context, args, operationLock) {
|
|
|
1068
1254
|
async function loadPortabilityAdapter(context) {
|
|
1069
1255
|
const override = join(context.sourceRoot, "scripts", "smrt-portability.mjs");
|
|
1070
1256
|
if (existsSync(override)) return await import(pathToFileURL(override).href);
|
|
1071
|
-
return import("./portability-
|
|
1257
|
+
return import("./portability-CES1YC2m.js").then((n) => n.s);
|
|
1072
1258
|
}
|
|
1073
1259
|
async function portability(context, operation, args, operationLock) {
|
|
1074
1260
|
const adapter = await loadPortabilityAdapter(context);
|
|
@@ -1104,7 +1290,7 @@ async function portability(context, operation, args, operationLock) {
|
|
|
1104
1290
|
...portabilityContext,
|
|
1105
1291
|
path: artifactPath
|
|
1106
1292
|
});
|
|
1107
|
-
printJson(context, {
|
|
1293
|
+
printJson$1(context, {
|
|
1108
1294
|
schemaVersion: 1,
|
|
1109
1295
|
status: `${operation}ed`,
|
|
1110
1296
|
...result
|
|
@@ -1130,7 +1316,9 @@ async function runApplicationOperation(context, operation, args) {
|
|
|
1130
1316
|
});
|
|
1131
1317
|
return 0;
|
|
1132
1318
|
case "setup":
|
|
1133
|
-
await withOperationLock(preparedStateRoot(context), operation, (lock) =>
|
|
1319
|
+
await withOperationLock(preparedStateRoot(context), operation, async (lock) => {
|
|
1320
|
+
printOnboardingUrl(context, (await setup(context, lock)).onboardingUrl);
|
|
1321
|
+
});
|
|
1134
1322
|
return 0;
|
|
1135
1323
|
case "recover":
|
|
1136
1324
|
await assertLocalOperation(context, "app:recover");
|
|
@@ -1149,7 +1337,7 @@ async function runApplicationOperation(context, operation, args) {
|
|
|
1149
1337
|
await withOperationLock(preparedStateRoot(context), operation, () => stop(context));
|
|
1150
1338
|
return 0;
|
|
1151
1339
|
case "backup":
|
|
1152
|
-
await
|
|
1340
|
+
await backup(context, args);
|
|
1153
1341
|
return 0;
|
|
1154
1342
|
case "export":
|
|
1155
1343
|
case "import":
|
|
@@ -1158,6 +1346,137 @@ async function runApplicationOperation(context, operation, args) {
|
|
|
1158
1346
|
}
|
|
1159
1347
|
}
|
|
1160
1348
|
//#endregion
|
|
1349
|
+
//#region src/app/tokens.ts
|
|
1350
|
+
/**
|
|
1351
|
+
* `smrt app token` — owner-minted bearer tokens for local MCP clients.
|
|
1352
|
+
*
|
|
1353
|
+
* ```
|
|
1354
|
+
* smrt app token --scopes notes.read [--expires 30d] [--label "Claude Desktop"]
|
|
1355
|
+
* smrt app token list
|
|
1356
|
+
* smrt app token revoke <id>
|
|
1357
|
+
* ```
|
|
1358
|
+
*
|
|
1359
|
+
* Local profile only. The token is printed once, in the `issued` JSON
|
|
1360
|
+
* document on stdout, and is otherwise never written anywhere: the runtime
|
|
1361
|
+
* stores only its HMAC. `list` and `revoke` output carries no token values.
|
|
1362
|
+
*/
|
|
1363
|
+
/** Usage text for `smrt app token`. */
|
|
1364
|
+
var TOKEN_USAGE = "Usage: smrt app token [create] --scopes <scope[,scope...]> [--expires <n>s|m|h|d] [--label <text>] | token list | token revoke <id>";
|
|
1365
|
+
var UNIT_SECONDS = {
|
|
1366
|
+
s: 1,
|
|
1367
|
+
m: 60,
|
|
1368
|
+
h: 3600,
|
|
1369
|
+
d: 1440 * 60
|
|
1370
|
+
};
|
|
1371
|
+
/** Parse `30d`, `12h`, `90m`, `3600s` or bare seconds. */
|
|
1372
|
+
function parseTokenLifetime(value) {
|
|
1373
|
+
const match = /^([1-9][0-9]{0,8})([smhd]?)$/u.exec(value.trim());
|
|
1374
|
+
if (!match) throw new Error(`Invalid --expires value. ${TOKEN_USAGE}`);
|
|
1375
|
+
return Number(match[1]) * UNIT_SECONDS[match[2] || "s"];
|
|
1376
|
+
}
|
|
1377
|
+
function parseCreateArgs(args) {
|
|
1378
|
+
const scopes = [];
|
|
1379
|
+
let expiresInSeconds;
|
|
1380
|
+
let label;
|
|
1381
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1382
|
+
const arg = args[index];
|
|
1383
|
+
const equals = arg.indexOf("=");
|
|
1384
|
+
const name = equals > 0 ? arg.slice(0, equals) : arg;
|
|
1385
|
+
if (![
|
|
1386
|
+
"--scopes",
|
|
1387
|
+
"--scope",
|
|
1388
|
+
"--expires",
|
|
1389
|
+
"--label"
|
|
1390
|
+
].includes(name)) throw new Error(`Unknown token option: ${name}. ${TOKEN_USAGE}`);
|
|
1391
|
+
const value = equals > 0 ? arg.slice(equals + 1) : args[++index];
|
|
1392
|
+
if (value === void 0 || equals < 0 && value.startsWith("--")) throw new Error(`Token option ${name} requires a value. ${TOKEN_USAGE}`);
|
|
1393
|
+
if (name === "--scopes" || name === "--scope") scopes.push(...value.split(/[\s,]+/u).filter(Boolean));
|
|
1394
|
+
else if (name === "--expires") {
|
|
1395
|
+
if (expiresInSeconds !== void 0) throw new Error("Token option --expires was provided more than once.");
|
|
1396
|
+
expiresInSeconds = parseTokenLifetime(value);
|
|
1397
|
+
} else {
|
|
1398
|
+
if (label !== void 0) throw new Error("Token option --label was provided more than once.");
|
|
1399
|
+
label = value;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
if (scopes.length === 0) throw new Error(`At least one scope is required. ${TOKEN_USAGE}`);
|
|
1403
|
+
return {
|
|
1404
|
+
scopes,
|
|
1405
|
+
expiresInSeconds,
|
|
1406
|
+
label
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
function printJson(context, value) {
|
|
1410
|
+
context.io.stdout(`${JSON.stringify(value, null, 2)}\n`);
|
|
1411
|
+
}
|
|
1412
|
+
/** Run `smrt app token <args…>` and resolve to the exit code. */
|
|
1413
|
+
async function runTokenOperation(context, args) {
|
|
1414
|
+
const [first, ...rest] = args;
|
|
1415
|
+
if (first === "help" || first === "--help" || first === "-h") {
|
|
1416
|
+
context.io.stdout(`${TOKEN_USAGE}\n`);
|
|
1417
|
+
return 0;
|
|
1418
|
+
}
|
|
1419
|
+
const subcommand = first === void 0 || first.startsWith("--") ? "create" : first;
|
|
1420
|
+
const subArgs = subcommand === first ? rest : args;
|
|
1421
|
+
if (![
|
|
1422
|
+
"create",
|
|
1423
|
+
"list",
|
|
1424
|
+
"revoke"
|
|
1425
|
+
].includes(subcommand)) throw new Error(`Unknown token operation: ${subcommand}. ${TOKEN_USAGE}`);
|
|
1426
|
+
const create = subcommand === "create" ? parseCreateArgs(subArgs) : null;
|
|
1427
|
+
if (subcommand === "list" && subArgs.length > 0) throw new Error(`token list takes no arguments. ${TOKEN_USAGE}`);
|
|
1428
|
+
if (subcommand === "revoke" && subArgs.length !== 1) throw new Error(`token revoke takes exactly one token id. ${TOKEN_USAGE}`);
|
|
1429
|
+
await assertLocalOperation(context, "app:token");
|
|
1430
|
+
const open = context.deps.runtime.openLocalMcpTokenStore ?? openLocalMcpTokenStore;
|
|
1431
|
+
const withStore = async (fn) => {
|
|
1432
|
+
const store = await open({
|
|
1433
|
+
appId: context.appId,
|
|
1434
|
+
dataDirectory: process.env.SMRT_DATA_DIR,
|
|
1435
|
+
sourceRoot: context.sourceRoot
|
|
1436
|
+
});
|
|
1437
|
+
try {
|
|
1438
|
+
return await fn(store);
|
|
1439
|
+
} finally {
|
|
1440
|
+
await store.close();
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
if (subcommand === "list") {
|
|
1444
|
+
printJson(context, {
|
|
1445
|
+
schemaVersion: 1,
|
|
1446
|
+
status: "ok",
|
|
1447
|
+
tokens: await withStore((store) => store.list()),
|
|
1448
|
+
secretValuesIncluded: false
|
|
1449
|
+
});
|
|
1450
|
+
return 0;
|
|
1451
|
+
}
|
|
1452
|
+
return withOperationLock(preparedStateRoot(context), `token-${subcommand}`, async () => {
|
|
1453
|
+
if (create) {
|
|
1454
|
+
const issued = await withStore((store) => store.issue(create));
|
|
1455
|
+
printJson(context, {
|
|
1456
|
+
schemaVersion: 1,
|
|
1457
|
+
status: "issued",
|
|
1458
|
+
id: issued.id,
|
|
1459
|
+
token: issued.token,
|
|
1460
|
+
scopes: issued.scopes,
|
|
1461
|
+
label: issued.label,
|
|
1462
|
+
createdAt: issued.createdAt,
|
|
1463
|
+
expiresAt: issued.expiresAt,
|
|
1464
|
+
notice: "This token is shown once and only its hash is stored. Put it in your MCP client configuration now; revoke it with smrt app token revoke <id>.",
|
|
1465
|
+
secretValuesIncluded: true
|
|
1466
|
+
});
|
|
1467
|
+
return 0;
|
|
1468
|
+
}
|
|
1469
|
+
const id = subArgs[0];
|
|
1470
|
+
printJson(context, {
|
|
1471
|
+
schemaVersion: 1,
|
|
1472
|
+
status: await withStore((store) => store.revoke(id)),
|
|
1473
|
+
id,
|
|
1474
|
+
secretValuesIncluded: false
|
|
1475
|
+
});
|
|
1476
|
+
return 0;
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
//#endregion
|
|
1161
1480
|
//#region src/app/cli.ts
|
|
1162
1481
|
/**
|
|
1163
1482
|
* `smrt app <operation>` dispatcher.
|
|
@@ -1188,6 +1507,7 @@ var APP_COMMANDS = Object.freeze({
|
|
|
1188
1507
|
"export [path]": "Write a logical, asset-aware export bundle",
|
|
1189
1508
|
"import <path>": "Import a logical export bundle into an empty application",
|
|
1190
1509
|
migrate: "Prepare storage custody and run smrt db:migrate under the lock",
|
|
1510
|
+
"token [list|revoke <id>] [--scopes ...]": "Local: issue, list, or revoke a scoped bearer token for a local MCP client",
|
|
1191
1511
|
"worker [task|schedule]": "Deployed: run the task or schedule worker",
|
|
1192
1512
|
"dev [...vite args]": "Run the application's Vite dev server",
|
|
1193
1513
|
"build [...vite args]": "Validate ./mcp-apps when present, then run the Vite build",
|
|
@@ -1202,6 +1522,10 @@ function errorEnvelope(error) {
|
|
|
1202
1522
|
code: "operation-failed",
|
|
1203
1523
|
...runtimeCode ? { runtimeCode } : {},
|
|
1204
1524
|
message: redactSecrets(error instanceof Error ? error.message : "Application operation failed."),
|
|
1525
|
+
...error instanceof ApplicationStartError ? {
|
|
1526
|
+
output: boundedTail(redactSecrets(error.output, process.env, { strict: true }), START_OUTPUT_TAIL_BYTES),
|
|
1527
|
+
logFile: error.logFile
|
|
1528
|
+
} : {},
|
|
1205
1529
|
recovery: APP_RECOVERY,
|
|
1206
1530
|
secretValuesIncluded: false
|
|
1207
1531
|
};
|
|
@@ -1214,6 +1538,7 @@ function renderAppHelp() {
|
|
|
1214
1538
|
var ENV_FILE_OPERATIONS = /* @__PURE__ */ new Set([
|
|
1215
1539
|
...APP_OPERATIONS,
|
|
1216
1540
|
"migrate",
|
|
1541
|
+
"token",
|
|
1217
1542
|
"dev",
|
|
1218
1543
|
"build",
|
|
1219
1544
|
"vite"
|
|
@@ -1227,7 +1552,8 @@ var ENV_FILE_OPERATIONS = /* @__PURE__ */ new Set([
|
|
|
1227
1552
|
async function runAppCommand(argv, options = {}) {
|
|
1228
1553
|
const io = {
|
|
1229
1554
|
stdout: options.io?.stdout ?? ((text) => void process.stdout.write(text)),
|
|
1230
|
-
stderr: options.io?.stderr ?? ((text) => void process.stderr.write(text))
|
|
1555
|
+
stderr: options.io?.stderr ?? ((text) => void process.stderr.write(text)),
|
|
1556
|
+
operatorTerminal: options.io ? options.io.operatorTerminal : process.stderr.isTTY && process.stdout.isTTY ? (text) => void process.stderr.write(text) : void 0
|
|
1231
1557
|
};
|
|
1232
1558
|
const operation = argv[0] || "doctor";
|
|
1233
1559
|
const rawArgs = argv.slice(1);
|
|
@@ -1250,7 +1576,7 @@ async function runAppCommand(argv, options = {}) {
|
|
|
1250
1576
|
const status = await validateMcpAppsIfPresent(context);
|
|
1251
1577
|
if (status !== 0) return status;
|
|
1252
1578
|
}
|
|
1253
|
-
return launchVite(context, operation === "vite" ? args : [operation, ...args]);
|
|
1579
|
+
return launchVite(context, operation === "vite" ? args : operation === "dev" ? ["dev", ...devServerArguments(args)] : [operation, ...args]);
|
|
1254
1580
|
}
|
|
1255
1581
|
const deps = {
|
|
1256
1582
|
...await defaultDependencies(sourceRoot),
|
|
@@ -1264,6 +1590,7 @@ async function runAppCommand(argv, options = {}) {
|
|
|
1264
1590
|
};
|
|
1265
1591
|
if (operation === "migrate") return await prepareMigration(context);
|
|
1266
1592
|
if (operation === "worker") return await runWorker(context, args);
|
|
1593
|
+
if (operation === "token") return await runTokenOperation(context, args);
|
|
1267
1594
|
if (!APP_OPERATIONS.includes(operation)) throw new Error(`Unknown app operation: ${operation}`);
|
|
1268
1595
|
return await runApplicationOperation(context, operation, args);
|
|
1269
1596
|
} catch (error) {
|
|
@@ -1272,4 +1599,4 @@ async function runAppCommand(argv, options = {}) {
|
|
|
1272
1599
|
}
|
|
1273
1600
|
}
|
|
1274
1601
|
//#endregion
|
|
1275
|
-
export {
|
|
1602
|
+
export { assertExternalArtifactPath as A, prepareMigration as C, cliExecutablePath as D, validateMcpAppsIfPresent as E, AppCommandError as F, ApplicationStartError as I, errorCode as L, resolveApplicationId as M, resolveApplicationStateRoot as N, runtimeEnvironment as O, runtimeConfigurationFingerprint as P, redactSecrets as R, launchVite as S, runWorker as T, sendTerminationSignal as _, renderAppHelp as a, ViteLauncherError as b, parseTokenLifetime as c, START_OUTPUT_TAIL_BYTES as d, runApplicationOperation as f, readOwnedProcess as g, matchesApplicationProcess as h, errorEnvelope as i, prepareApplicationStateRoot as j, webLauncherPath as k, runTokenOperation as l, checkOwnedProcess as m, APP_RECOVERY as n, runAppCommand as o, WEB_LAUNCHER_NAME as p, cli_exports as r, TOKEN_USAGE as s, APP_COMMANDS as t, APP_OPERATIONS as u, verifyOwnedProcess as v, resolveViteEntry as w, devServerArguments as x, writeProcessRecord as y };
|