@m8t-stack/cli 0.2.30 → 0.2.32
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/cli.js +710 -159
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1247,7 +1247,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1247
1247
|
import { Builtins, Cli } from "clipanion";
|
|
1248
1248
|
|
|
1249
1249
|
// src/lib/package-version.ts
|
|
1250
|
-
var CLI_VERSION = "0.2.
|
|
1250
|
+
var CLI_VERSION = "0.2.32";
|
|
1251
1251
|
|
|
1252
1252
|
// src/lib/render-error.ts
|
|
1253
1253
|
init_errors();
|
|
@@ -10645,9 +10645,9 @@ var Webhooks = class extends APIResource {
|
|
|
10645
10645
|
__classPrivateFieldGet(this, _Webhooks_instances, "m", _Webhooks_validateSecret).call(this, secret);
|
|
10646
10646
|
const headersObj = buildHeaders([headers]).values;
|
|
10647
10647
|
const signatureHeader = __classPrivateFieldGet(this, _Webhooks_instances, "m", _Webhooks_getRequiredHeader).call(this, headersObj, "webhook-signature");
|
|
10648
|
-
const
|
|
10648
|
+
const timestamp2 = __classPrivateFieldGet(this, _Webhooks_instances, "m", _Webhooks_getRequiredHeader).call(this, headersObj, "webhook-timestamp");
|
|
10649
10649
|
const webhookId = __classPrivateFieldGet(this, _Webhooks_instances, "m", _Webhooks_getRequiredHeader).call(this, headersObj, "webhook-id");
|
|
10650
|
-
const timestampSeconds = parseInt(
|
|
10650
|
+
const timestampSeconds = parseInt(timestamp2, 10);
|
|
10651
10651
|
if (isNaN(timestampSeconds)) {
|
|
10652
10652
|
throw new InvalidWebhookSignatureError("Invalid webhook timestamp format");
|
|
10653
10653
|
}
|
|
@@ -10660,7 +10660,7 @@ var Webhooks = class extends APIResource {
|
|
|
10660
10660
|
}
|
|
10661
10661
|
const signatures = signatureHeader.split(" ").map((part) => part.startsWith("v1,") ? part.substring(3) : part);
|
|
10662
10662
|
const decodedSecret = secret.startsWith("whsec_") ? Buffer.from(secret.replace("whsec_", ""), "base64") : Buffer.from(secret, "utf-8");
|
|
10663
|
-
const signedPayload = webhookId ? `${webhookId}.${
|
|
10663
|
+
const signedPayload = webhookId ? `${webhookId}.${timestamp2}.${payload}` : `${timestamp2}.${payload}`;
|
|
10664
10664
|
const key2 = await crypto.subtle.importKey("raw", decodedSecret, { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
|
|
10665
10665
|
for (const signature of signatures) {
|
|
10666
10666
|
try {
|
|
@@ -17000,7 +17000,7 @@ var SIZE_PRESETS = {
|
|
|
17000
17000
|
};
|
|
17001
17001
|
var DEFAULT_REGISTRY = "ghcr.io/m8t-labs";
|
|
17002
17002
|
var DEFAULT_IMAGE = "m8t-coding-agent";
|
|
17003
|
-
var DEFAULT_TAG = "v0.1.
|
|
17003
|
+
var DEFAULT_TAG = "v0.1.2";
|
|
17004
17004
|
var DEFAULT_MODEL = "gpt-4.1-mini";
|
|
17005
17005
|
var NAME_RE = /^[a-z0-9-]+$/;
|
|
17006
17006
|
var CoderDeployCommand = class extends M8tCommand {
|
|
@@ -18088,9 +18088,7 @@ function extractTarGz(tgz, into) {
|
|
|
18088
18088
|
}
|
|
18089
18089
|
|
|
18090
18090
|
// src/lib/platform-stamp.ts
|
|
18091
|
-
import { TableClient
|
|
18092
|
-
init_errors();
|
|
18093
|
-
init_rbac();
|
|
18091
|
+
import { TableClient as TableClient2 } from "@azure/data-tables";
|
|
18094
18092
|
|
|
18095
18093
|
// src/lib/platform-storage-discovery.ts
|
|
18096
18094
|
init_http();
|
|
@@ -18118,7 +18116,19 @@ async function discoverStampStorage(opts) {
|
|
|
18118
18116
|
return { tableEndpoint, accountResourceId: chosen.id, accountName: chosen.name };
|
|
18119
18117
|
}
|
|
18120
18118
|
|
|
18121
|
-
// src/lib/
|
|
18119
|
+
// src/lib/metadata-table-write.ts
|
|
18120
|
+
import { TableClient, AzureNamedKeyCredential } from "@azure/data-tables";
|
|
18121
|
+
init_errors();
|
|
18122
|
+
init_rbac();
|
|
18123
|
+
function is403(e) {
|
|
18124
|
+
return e.statusCode === 403;
|
|
18125
|
+
}
|
|
18126
|
+
function is404(e) {
|
|
18127
|
+
return e.statusCode === 404;
|
|
18128
|
+
}
|
|
18129
|
+
function sleep2(ms) {
|
|
18130
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
18131
|
+
}
|
|
18122
18132
|
async function makeSharedKeyClient(opts) {
|
|
18123
18133
|
const rg = /\/resourceGroups\/([^/]+)\//i.exec(opts.accountResourceId)?.[1];
|
|
18124
18134
|
if (!rg) return null;
|
|
@@ -18126,22 +18136,29 @@ async function makeSharedKeyClient(opts) {
|
|
|
18126
18136
|
if (!key2) return null;
|
|
18127
18137
|
return new TableClient(opts.tableEndpoint, TABLE, new AzureNamedKeyCredential(opts.accountName, key2));
|
|
18128
18138
|
}
|
|
18129
|
-
async function
|
|
18130
|
-
const
|
|
18131
|
-
const
|
|
18132
|
-
const
|
|
18139
|
+
async function upsertMetadataEntity(opts) {
|
|
18140
|
+
const discover = opts.discoverImpl ?? discoverStampStorage;
|
|
18141
|
+
const makeAad = opts.aadClientImpl ?? ((endpoint, credential2) => new TableClient(endpoint, TABLE, credential2));
|
|
18142
|
+
const makeShared = opts.sharedClientImpl ?? makeSharedKeyClient;
|
|
18143
|
+
const nap = opts.sleepImpl ?? sleep2;
|
|
18144
|
+
const { tableEndpoint, accountResourceId, accountName } = await discover({
|
|
18145
|
+
credential: opts.credential,
|
|
18146
|
+
subscriptionId: opts.subscriptionId,
|
|
18147
|
+
resourceGroup: opts.resourceGroup
|
|
18148
|
+
});
|
|
18149
|
+
const aad = makeAad(tableEndpoint, opts.credential);
|
|
18133
18150
|
await aad.createTable().catch(() => void 0);
|
|
18134
18151
|
try {
|
|
18135
|
-
await aad.upsertEntity(entity, "Replace");
|
|
18152
|
+
await aad.upsertEntity(opts.entity, "Replace");
|
|
18136
18153
|
return;
|
|
18137
18154
|
} catch (e) {
|
|
18138
18155
|
if (!is403(e)) throw e;
|
|
18139
18156
|
}
|
|
18140
|
-
opts.onProgress?.(
|
|
18141
|
-
const shared = await
|
|
18157
|
+
opts.onProgress?.(`writing the ${opts.label} with the account key \u2014 your identity lacks the Storage Table data role.`);
|
|
18158
|
+
const shared = await makeShared({ tableEndpoint, accountResourceId, accountName });
|
|
18142
18159
|
if (shared) {
|
|
18143
18160
|
await shared.createTable().catch(() => void 0);
|
|
18144
|
-
await shared.upsertEntity(entity, "Replace");
|
|
18161
|
+
await shared.upsertEntity(opts.entity, "Replace");
|
|
18145
18162
|
return;
|
|
18146
18163
|
}
|
|
18147
18164
|
opts.onProgress?.("shared-key access is disabled \u2014 granting Storage Table Data Contributor to your identity (one-time)\u2026");
|
|
@@ -18155,30 +18172,45 @@ async function writeStamp(opts) {
|
|
|
18155
18172
|
});
|
|
18156
18173
|
} catch (e) {
|
|
18157
18174
|
throw new LocalCliError({
|
|
18158
|
-
code:
|
|
18159
|
-
message: `Cannot write the
|
|
18175
|
+
code: opts.errorCode,
|
|
18176
|
+
message: `Cannot write the ${opts.label}: AAD lacks the Storage Table data role for ${accountName}, shared-key access is disabled, and self-granting the role failed.`,
|
|
18160
18177
|
hint: "Grant yourself 'Storage Table Data Contributor' on the storage account and retry.",
|
|
18161
18178
|
cause: e
|
|
18162
18179
|
});
|
|
18163
18180
|
}
|
|
18164
18181
|
for (let i = 0; i < 6; i++) {
|
|
18165
|
-
await
|
|
18182
|
+
await nap(1e4);
|
|
18166
18183
|
try {
|
|
18167
|
-
await aad.upsertEntity(entity, "Replace");
|
|
18184
|
+
await aad.upsertEntity(opts.entity, "Replace");
|
|
18168
18185
|
return;
|
|
18169
18186
|
} catch (e) {
|
|
18170
18187
|
if (!is403(e)) throw e;
|
|
18171
18188
|
}
|
|
18172
18189
|
}
|
|
18173
18190
|
throw new LocalCliError({
|
|
18174
|
-
code:
|
|
18175
|
-
message: `Cannot write the
|
|
18176
|
-
hint:
|
|
18191
|
+
code: opts.errorCode,
|
|
18192
|
+
message: `Cannot write the ${opts.label}: granted 'Storage Table Data Contributor' on ${accountName}, but the data-plane role has not propagated yet.`,
|
|
18193
|
+
hint: opts.retryHint
|
|
18194
|
+
});
|
|
18195
|
+
}
|
|
18196
|
+
|
|
18197
|
+
// src/lib/platform-stamp.ts
|
|
18198
|
+
async function writeStamp(opts) {
|
|
18199
|
+
const entity = stampToEntity(opts.stamp);
|
|
18200
|
+
await upsertMetadataEntity({
|
|
18201
|
+
credential: opts.credential,
|
|
18202
|
+
subscriptionId: opts.subscriptionId,
|
|
18203
|
+
resourceGroup: opts.resourceGroup,
|
|
18204
|
+
entity,
|
|
18205
|
+
label: "platform stamp",
|
|
18206
|
+
errorCode: "PLATFORM_STAMP_NO_ACCESS",
|
|
18207
|
+
retryHint: "The role assignment persists \u2014 retry 'm8t platform update' in a few minutes.",
|
|
18208
|
+
...opts.onProgress ? { onProgress: opts.onProgress } : {}
|
|
18177
18209
|
});
|
|
18178
18210
|
}
|
|
18179
18211
|
async function readStamp(opts) {
|
|
18180
18212
|
const { tableEndpoint, accountResourceId, accountName } = await discoverStampStorage(opts);
|
|
18181
|
-
const aad = new
|
|
18213
|
+
const aad = new TableClient2(tableEndpoint, TABLE, opts.credential);
|
|
18182
18214
|
try {
|
|
18183
18215
|
const row = await aad.getEntity(PK, RK);
|
|
18184
18216
|
return entityToStamp(row);
|
|
@@ -18195,15 +18227,6 @@ async function readStamp(opts) {
|
|
|
18195
18227
|
return null;
|
|
18196
18228
|
}
|
|
18197
18229
|
}
|
|
18198
|
-
function is403(e) {
|
|
18199
|
-
return e.statusCode === 403;
|
|
18200
|
-
}
|
|
18201
|
-
function is404(e) {
|
|
18202
|
-
return e.statusCode === 404;
|
|
18203
|
-
}
|
|
18204
|
-
function sleep2(ms) {
|
|
18205
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
18206
|
-
}
|
|
18207
18230
|
|
|
18208
18231
|
// src/lib/platform-converge.ts
|
|
18209
18232
|
import * as fs22 from "fs";
|
|
@@ -18245,6 +18268,7 @@ function buildBicepParams(p) {
|
|
|
18245
18268
|
if (p.installerImage !== void 0) params.push(`installerImage=${p.installerImage}`);
|
|
18246
18269
|
if (p.updateCron !== void 0) params.push(`updateCron=${p.updateCron}`);
|
|
18247
18270
|
if (p.channelUrl !== void 0) params.push(`channelUrl=${p.channelUrl}`);
|
|
18271
|
+
if (p.voiceInternalSecret) params.push(`voiceInternalSecret=${p.voiceInternalSecret}`);
|
|
18248
18272
|
if (p.gatewayCpu) params.push(`gatewayCpu=${p.gatewayCpu}`);
|
|
18249
18273
|
if (p.gatewayMemory) params.push(`gatewayMemory=${p.gatewayMemory}`);
|
|
18250
18274
|
if (p.refereeEnabled) params.push(`refereeEnabled=${p.refereeEnabled}`);
|
|
@@ -18664,7 +18688,7 @@ async function resolveBicepParamsForConverge(ctx, opts = {}) {
|
|
|
18664
18688
|
hint: "Pass --suffix <existing-suffix> (from the deployed resource names) to avoid provisioning duplicate resources."
|
|
18665
18689
|
});
|
|
18666
18690
|
}
|
|
18667
|
-
const location = (await runAz(["resource", "list", "-g", ctx.resourceGroup, "--query", "[0].location", "-o", "tsv"])).trim();
|
|
18691
|
+
const location = opts.location ?? (await runAz(["resource", "list", "-g", ctx.resourceGroup, "--query", "[0].location", "-o", "tsv"])).trim();
|
|
18668
18692
|
const foundryResourceId = await resolveFoundryResourceId(opts.foundryEndpoint ?? "", opts.foundryResourceId);
|
|
18669
18693
|
const acrPullIdentityResourceId = resolveAcrPullIdentity({ explicit: opts.acrPullIdentity });
|
|
18670
18694
|
const imageRef = `${ctx.manifest.components.gateway.ref}:${ctx.manifest.components.gateway.tag}`;
|
|
@@ -18680,7 +18704,9 @@ async function resolveBicepParamsForConverge(ctx, opts = {}) {
|
|
|
18680
18704
|
acrPullIdentityResourceId,
|
|
18681
18705
|
acrResourceId,
|
|
18682
18706
|
foundryTracingMode: opts.foundryTracingMode,
|
|
18683
|
-
...opts.assignSubscriptionRoles !== void 0 ? { assignSubscriptionRoles: opts.assignSubscriptionRoles } : {}
|
|
18707
|
+
...opts.assignSubscriptionRoles !== void 0 ? { assignSubscriptionRoles: opts.assignSubscriptionRoles } : {},
|
|
18708
|
+
...opts.voiceInternalSecret ? { voiceInternalSecret: opts.voiceInternalSecret } : {},
|
|
18709
|
+
...opts.channelUrl ? { channelUrl: opts.channelUrl } : {}
|
|
18684
18710
|
};
|
|
18685
18711
|
}
|
|
18686
18712
|
function swapHostedImage(def, newImage) {
|
|
@@ -18949,10 +18975,10 @@ import { DefaultAzureCredential as DefaultAzureCredential17 } from "@azure/ident
|
|
|
18949
18975
|
import * as path25 from "path";
|
|
18950
18976
|
|
|
18951
18977
|
// src/lib/platform-infra-params.ts
|
|
18952
|
-
import { TableClient as
|
|
18978
|
+
import { TableClient as TableClient3 } from "@azure/data-tables";
|
|
18953
18979
|
async function openInfraParamsTable(opts) {
|
|
18954
18980
|
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
18955
|
-
return new
|
|
18981
|
+
return new TableClient3(tableEndpoint, "Metadata", opts.credential);
|
|
18956
18982
|
}
|
|
18957
18983
|
async function readInfraParams(client) {
|
|
18958
18984
|
try {
|
|
@@ -18961,8 +18987,17 @@ async function readInfraParams(client) {
|
|
|
18961
18987
|
return null;
|
|
18962
18988
|
}
|
|
18963
18989
|
}
|
|
18964
|
-
async function writeInfraParams(
|
|
18965
|
-
await
|
|
18990
|
+
async function writeInfraParams(ctx, p) {
|
|
18991
|
+
await upsertMetadataEntity({
|
|
18992
|
+
credential: ctx.credential,
|
|
18993
|
+
subscriptionId: ctx.subscriptionId,
|
|
18994
|
+
resourceGroup: ctx.resourceGroup,
|
|
18995
|
+
entity: infraParamsToEntity(p),
|
|
18996
|
+
label: "infra-params row",
|
|
18997
|
+
errorCode: "PLATFORM_INFRA_PARAMS_NO_ACCESS",
|
|
18998
|
+
retryHint: ctx.retryHint,
|
|
18999
|
+
...ctx.onProgress ? { onProgress: ctx.onProgress } : {}
|
|
19000
|
+
});
|
|
18966
19001
|
}
|
|
18967
19002
|
async function deriveSuffixVerified(opts) {
|
|
18968
19003
|
const list = JSON.parse(
|
|
@@ -18999,6 +19034,24 @@ async function deriveSuffixVerified(opts) {
|
|
|
18999
19034
|
return cae.length === 1 ? suffix : null;
|
|
19000
19035
|
}
|
|
19001
19036
|
|
|
19037
|
+
// src/lib/gateway-live-params.ts
|
|
19038
|
+
async function readLiveGatewayEnvVar(opts) {
|
|
19039
|
+
const args = ["containerapp", "show", "-n", opts.gatewayName, "-g", opts.resourceGroup];
|
|
19040
|
+
if (opts.subscriptionId) args.push("--subscription", opts.subscriptionId);
|
|
19041
|
+
args.push("--query", `properties.template.containers[0].env[?name=='${opts.name}'].value | [0]`, "-o", "tsv");
|
|
19042
|
+
let raw;
|
|
19043
|
+
try {
|
|
19044
|
+
raw = await runAz(args);
|
|
19045
|
+
} catch {
|
|
19046
|
+
return "";
|
|
19047
|
+
}
|
|
19048
|
+
const v = raw.trim();
|
|
19049
|
+
return v === "None" ? "" : v;
|
|
19050
|
+
}
|
|
19051
|
+
function readLiveVoiceInternalSecret(opts) {
|
|
19052
|
+
return readLiveGatewayEnvVar({ ...opts, name: "M8T_INTERNAL_SECRET" });
|
|
19053
|
+
}
|
|
19054
|
+
|
|
19002
19055
|
// src/lib/platform-converge-cli.ts
|
|
19003
19056
|
init_errors();
|
|
19004
19057
|
|
|
@@ -19412,6 +19465,17 @@ async function buildConvergeDeps(args) {
|
|
|
19412
19465
|
const infraTable = await openInfraParamsTable({ credential: args.credential, subscriptionId: args.subscriptionId, resourceGroup: args.resourceGroup });
|
|
19413
19466
|
const stamped = (await readInfraParams(infraTable))?.bicep;
|
|
19414
19467
|
args.onProgress?.("subscription-scoped modules skipped: updater is RG-scoped; sub-level changes require a founder-run CLI converge.");
|
|
19468
|
+
const gatewayName = args.suffix ? `m8t-gateway-${args.suffix}` : void 0;
|
|
19469
|
+
const voiceInternalSecret = gatewayName ? await readLiveVoiceInternalSecret({
|
|
19470
|
+
gatewayName,
|
|
19471
|
+
resourceGroup: args.resourceGroup,
|
|
19472
|
+
subscriptionId: args.subscriptionId
|
|
19473
|
+
}) : "";
|
|
19474
|
+
if (gatewayName && !voiceInternalSecret) {
|
|
19475
|
+
args.onProgress?.(
|
|
19476
|
+
`could not read the live voice-relay secret (M8T_INTERNAL_SECRET) off gateway '${gatewayName}' \u2014 the read failed or the variable is missing. This infra converge will re-mint the secret, rolling BOTH the gateway and the voice relay.`
|
|
19477
|
+
);
|
|
19478
|
+
}
|
|
19415
19479
|
const opts = {
|
|
19416
19480
|
suffix: args.suffix,
|
|
19417
19481
|
assignSubscriptionRoles: false,
|
|
@@ -19422,8 +19486,11 @@ async function buildConvergeDeps(args) {
|
|
|
19422
19486
|
foundryResourceId: stamped.foundryResourceId,
|
|
19423
19487
|
acrPullIdentity: stamped.acrPullIdentityResourceId,
|
|
19424
19488
|
acrResourceId: stamped.acrResourceId,
|
|
19425
|
-
...stamped.foundryTracingMode ? { foundryTracingMode: stamped.foundryTracingMode } : {}
|
|
19426
|
-
|
|
19489
|
+
...stamped.foundryTracingMode ? { foundryTracingMode: stamped.foundryTracingMode } : {},
|
|
19490
|
+
...stamped.channelUrl ? { channelUrl: stamped.channelUrl } : {},
|
|
19491
|
+
...stamped.location ? { location: stamped.location } : {}
|
|
19492
|
+
} : {},
|
|
19493
|
+
...voiceInternalSecret ? { voiceInternalSecret } : {}
|
|
19427
19494
|
};
|
|
19428
19495
|
return applyInfraDeploy(a, ctx, target, opts);
|
|
19429
19496
|
},
|
|
@@ -19471,17 +19538,26 @@ async function buildConvergeDeps(args) {
|
|
|
19471
19538
|
const table = await openInfraParamsTable({ credential: args.credential, subscriptionId: args.subscriptionId, resourceGroup: args.resourceGroup });
|
|
19472
19539
|
const existing = await readInfraParams(table);
|
|
19473
19540
|
if (!existing) {
|
|
19474
|
-
warn("infra-params re-stamp skipped: no existing row to refresh (
|
|
19541
|
+
warn("infra-params re-stamp skipped: no existing row to refresh (enable-auto-update writes the authoritative row).");
|
|
19475
19542
|
return;
|
|
19476
19543
|
}
|
|
19477
|
-
await writeInfraParams(
|
|
19478
|
-
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19544
|
+
await writeInfraParams(
|
|
19545
|
+
{
|
|
19546
|
+
credential: args.credential,
|
|
19547
|
+
subscriptionId: args.subscriptionId,
|
|
19548
|
+
resourceGroup: args.resourceGroup,
|
|
19549
|
+
retryHint: "The role assignment persists \u2014 retry 'm8t platform update' in a few minutes.",
|
|
19550
|
+
onProgress: args.onProgress
|
|
19482
19551
|
},
|
|
19483
|
-
|
|
19484
|
-
|
|
19552
|
+
{
|
|
19553
|
+
...existing,
|
|
19554
|
+
bicep: {
|
|
19555
|
+
...existing.bicep,
|
|
19556
|
+
imageRef: `${ctx.manifest.components.gateway.ref}:${ctx.manifest.components.gateway.tag}`
|
|
19557
|
+
},
|
|
19558
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19559
|
+
}
|
|
19560
|
+
);
|
|
19485
19561
|
},
|
|
19486
19562
|
async applySeedRefresh(_a3, ctx) {
|
|
19487
19563
|
let kvUri;
|
|
@@ -19718,10 +19794,10 @@ function resolveHeadlessContextFromEnv(env) {
|
|
|
19718
19794
|
}
|
|
19719
19795
|
|
|
19720
19796
|
// src/lib/apply-request-store.ts
|
|
19721
|
-
import { TableClient as
|
|
19797
|
+
import { TableClient as TableClient4 } from "@azure/data-tables";
|
|
19722
19798
|
async function openApplyTable(opts) {
|
|
19723
19799
|
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
19724
|
-
return new
|
|
19800
|
+
return new TableClient4(tableEndpoint, "Metadata", opts.credential);
|
|
19725
19801
|
}
|
|
19726
19802
|
function hasStatus(e, n) {
|
|
19727
19803
|
return e.statusCode === n;
|
|
@@ -20111,10 +20187,11 @@ var PlatformConvergeCommand = class extends M8tCommand {
|
|
|
20111
20187
|
}
|
|
20112
20188
|
/**
|
|
20113
20189
|
* Recover the real bicep suffix WITHOUT guessing: prefer the stamped
|
|
20114
|
-
* `system/infra-params` row (written by
|
|
20115
|
-
* fall back to deriving it from the live
|
|
20116
|
-
*
|
|
20117
|
-
* both fail, throw rather than
|
|
20190
|
+
* `system/infra-params` row (written by enable-auto-update, and re-stamped
|
|
20191
|
+
* by every full converge), and fall back to deriving it from the live
|
|
20192
|
+
* gateway container-app name — but only when that derivation VERIFIES
|
|
20193
|
+
* (exactly one gateway + a matching CAE). If both fail, throw rather than
|
|
20194
|
+
* provision a duplicate stack.
|
|
20118
20195
|
*/
|
|
20119
20196
|
async recoverSuffix(ctx) {
|
|
20120
20197
|
const infraTable = await openInfraParamsTable({
|
|
@@ -20510,6 +20587,12 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
20510
20587
|
channelUrl = Option33.String("--channel-url", {
|
|
20511
20588
|
description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
|
|
20512
20589
|
});
|
|
20590
|
+
location = Option33.String("--location", {
|
|
20591
|
+
description: "Region for the updater identity + job. Defaults to the resource group's existing resources."
|
|
20592
|
+
});
|
|
20593
|
+
foundryTracing = Option33.String("--foundry-tracing", {
|
|
20594
|
+
description: "project | account | skip. Pass the value the install was deployed with \u2014 omitting it lets the bicep default (project) switch tracing on."
|
|
20595
|
+
});
|
|
20513
20596
|
endpoint = Option33.String("--endpoint", {
|
|
20514
20597
|
description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
|
|
20515
20598
|
});
|
|
@@ -20574,7 +20657,12 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
20574
20657
|
message: `Could not read the live image for gateway '${gatewayName}' in resource group ${resourceGroup}.`
|
|
20575
20658
|
});
|
|
20576
20659
|
}
|
|
20577
|
-
const
|
|
20660
|
+
const voiceInternalSecret = await readLiveVoiceInternalSecret({
|
|
20661
|
+
gatewayName,
|
|
20662
|
+
resourceGroup,
|
|
20663
|
+
subscriptionId
|
|
20664
|
+
});
|
|
20665
|
+
const location = typeof this.location === "string" && this.location.length > 0 ? this.location : (await runAz(["resource", "list", "-g", resourceGroup, "--subscription", subscriptionId, "--query", "[0].location", "-o", "tsv"])).trim();
|
|
20578
20666
|
if (!location) {
|
|
20579
20667
|
throw new LocalCliError({
|
|
20580
20668
|
code: "PLATFORM_ENABLE_AUTO_UPDATE_NO_LOCATION",
|
|
@@ -20593,7 +20681,9 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
20593
20681
|
foundryResourceId,
|
|
20594
20682
|
foundryProjectEndpoint: project.endpoint,
|
|
20595
20683
|
acrPullIdentityResourceId,
|
|
20596
|
-
acrResourceId
|
|
20684
|
+
acrResourceId,
|
|
20685
|
+
...parseFoundryTracingMode(this.foundryTracing) ? { foundryTracingMode: parseFoundryTracingMode(this.foundryTracing) } : {},
|
|
20686
|
+
...typeof this.channelUrl === "string" && this.channelUrl.length > 0 ? { channelUrl: this.channelUrl } : {}
|
|
20597
20687
|
};
|
|
20598
20688
|
onProgress("running Bicep deployment to provision the updater (~2-4 min)\u2026");
|
|
20599
20689
|
const repoRoot = await resolveRepoRoot2();
|
|
@@ -20604,18 +20694,27 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
20604
20694
|
...recoveredParams,
|
|
20605
20695
|
provisionUpdater: true,
|
|
20606
20696
|
installerImage: this.installerImage,
|
|
20607
|
-
...
|
|
20608
|
-
...typeof this.
|
|
20697
|
+
...voiceInternalSecret ? { voiceInternalSecret } : {},
|
|
20698
|
+
...typeof this.updateCron === "string" ? { updateCron: this.updateCron } : {}
|
|
20609
20699
|
}),
|
|
20610
20700
|
deploymentName: `m8t-enable-auto-update-${suffix.slice(0, 12)}`
|
|
20611
20701
|
});
|
|
20612
20702
|
onProgress("backfilling system/infra-params\u2026");
|
|
20613
|
-
await writeInfraParams(
|
|
20614
|
-
|
|
20615
|
-
|
|
20616
|
-
|
|
20617
|
-
|
|
20618
|
-
|
|
20703
|
+
await writeInfraParams(
|
|
20704
|
+
{
|
|
20705
|
+
credential: credential2,
|
|
20706
|
+
subscriptionId,
|
|
20707
|
+
resourceGroup,
|
|
20708
|
+
retryHint: "The role assignment persists \u2014 retry 'm8t platform enable-auto-update' in a few minutes.",
|
|
20709
|
+
onProgress
|
|
20710
|
+
},
|
|
20711
|
+
{
|
|
20712
|
+
schemaVersion: 1,
|
|
20713
|
+
suffix,
|
|
20714
|
+
bicep: recoveredParams,
|
|
20715
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20716
|
+
}
|
|
20717
|
+
);
|
|
20619
20718
|
if (mode === "json") {
|
|
20620
20719
|
this.context.stdout.write(renderJson({ suffix, resourceGroup, installerImage: this.installerImage, enabled: true }) + "\n");
|
|
20621
20720
|
return 0;
|
|
@@ -22589,7 +22688,7 @@ var OpenCommand = class extends M8tCommand {
|
|
|
22589
22688
|
// src/commands/dream/run.ts
|
|
22590
22689
|
import { Command as Command46, Option as Option43 } from "clipanion";
|
|
22591
22690
|
import { AzureCliCredential } from "@azure/identity";
|
|
22592
|
-
import { TableClient as
|
|
22691
|
+
import { TableClient as TableClient6 } from "@azure/data-tables";
|
|
22593
22692
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
22594
22693
|
import { LogsQueryClient as LogsQueryClient3, LogsQueryResultStatus as LogsQueryResultStatus3 } from "@azure/monitor-query";
|
|
22595
22694
|
|
|
@@ -22898,7 +22997,7 @@ var ConvFetchError = class extends Error {
|
|
|
22898
22997
|
};
|
|
22899
22998
|
|
|
22900
22999
|
// ../../packages/brain/engine/dist/esm/cursor.js
|
|
22901
|
-
import { TableClient as
|
|
23000
|
+
import { TableClient as TableClient5 } from "@azure/data-tables";
|
|
22902
23001
|
|
|
22903
23002
|
// ../../packages/agent-ledger/dist/esm/row-key.js
|
|
22904
23003
|
var MAX_MS = 864e13;
|
|
@@ -23038,7 +23137,7 @@ async function commitCursorAdvance(plan, cursor) {
|
|
|
23038
23137
|
}
|
|
23039
23138
|
var CURSOR_TABLE_NAME = "BrainDreamCursor";
|
|
23040
23139
|
function makeTableCursor(credential2, tableEndpoint) {
|
|
23041
|
-
const client = new
|
|
23140
|
+
const client = new TableClient5(tableEndpoint, CURSOR_TABLE_NAME, credential2);
|
|
23042
23141
|
return new TableCursor(client);
|
|
23043
23142
|
}
|
|
23044
23143
|
function formatCursorPreview(cursor) {
|
|
@@ -25022,7 +25121,7 @@ AppDependencies
|
|
|
25022
25121
|
};
|
|
25023
25122
|
}
|
|
25024
25123
|
function buildLiveSources(context, credential2) {
|
|
25025
|
-
const ledgerClient = new
|
|
25124
|
+
const ledgerClient = new TableClient6(context.ledgerTableEndpoint, LEDGER_TABLE_NAME, credential2);
|
|
25026
25125
|
const ledger = makeLedgerSource(
|
|
25027
25126
|
async (pk, sinceTs) => fetchLedgerRows(
|
|
25028
25127
|
{ workers: [pk], source: "all", from: sinceTs, to: "9999-12-31T23:59:59.999Z" },
|
|
@@ -25818,10 +25917,16 @@ function buildAciCreateArgs(s) {
|
|
|
25818
25917
|
`RESOURCE_GROUP=${s.resourceGroup}`,
|
|
25819
25918
|
`LOCATION=${s.location}`,
|
|
25820
25919
|
`MI_CLIENT_ID=${s.miClientId}`,
|
|
25821
|
-
`APP_REG_CLIENT_ID=${s.appRegClientId}
|
|
25920
|
+
`APP_REG_CLIENT_ID=${s.appRegClientId}`,
|
|
25921
|
+
// The installer's own image ref, so the install can provision the updater job
|
|
25922
|
+
// to run THIS engine. Derived from the launched image rather than threaded
|
|
25923
|
+
// separately, so the two can never disagree — and never read from the release
|
|
25924
|
+
// pin, which can name an installer several versions behind the running one.
|
|
25925
|
+
`INSTALLER_IMAGE_REF=${s.image}`
|
|
25822
25926
|
];
|
|
25823
25927
|
if (s.gatewayImageRef) env.push(`GATEWAY_IMAGE_REF=${s.gatewayImageRef}`);
|
|
25824
25928
|
if (s.foundryTracing) env.push(`FOUNDRY_TRACING=${s.foundryTracing}`);
|
|
25929
|
+
if (s.updateChannelUrl) env.push(`M8T_UPDATE_CHANNEL_URL=${s.updateChannelUrl}`);
|
|
25825
25930
|
if (s.enrollContactEmail) env.push(`M8T_ENROLL_CONTACT_EMAIL=${s.enrollContactEmail}`);
|
|
25826
25931
|
if (s.githubApp) {
|
|
25827
25932
|
const g = s.githubApp;
|
|
@@ -25973,7 +26078,7 @@ async function writeBootstrapState(state, home = os13.homedir()) {
|
|
|
25973
26078
|
// src/commands/bootstrap/launch.ts
|
|
25974
26079
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
25975
26080
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
25976
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
26081
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.38";
|
|
25977
26082
|
var ACI_NAME = "m8t-installer";
|
|
25978
26083
|
var MI_NAME = "m8t-installer-mi";
|
|
25979
26084
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
@@ -26090,7 +26195,8 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
26090
26195
|
gatewayImageRef,
|
|
26091
26196
|
foundryTracing: "skip",
|
|
26092
26197
|
githubApp,
|
|
26093
|
-
enrollContactEmail: account.upn
|
|
26198
|
+
enrollContactEmail: account.upn,
|
|
26199
|
+
...process.env.M8T_UPDATE_CHANNEL_URL ? { updateChannelUrl: process.env.M8T_UPDATE_CHANNEL_URL } : {}
|
|
26094
26200
|
});
|
|
26095
26201
|
this.context.stdout.write(
|
|
26096
26202
|
`${colors.success("\u2713")} installer launched in ${colors.field(resourceGroup)} (${location}).
|
|
@@ -26423,42 +26529,301 @@ var DEFAULT_MEMORY_INDEX_HEADER = [
|
|
|
26423
26529
|
`> Your memories, newest first. The summary on each line is usually enough to answer \u2014 open the linked file only when you need full detail. Each path is repo-root; copy it verbatim, never invent one.`,
|
|
26424
26530
|
``
|
|
26425
26531
|
].join("\n");
|
|
26426
|
-
|
|
26427
|
-
|
|
26428
|
-
|
|
26429
|
-
|
|
26430
|
-
|
|
26431
|
-
|
|
26532
|
+
var ONBOARDING_BLOCK_KEYS = [
|
|
26533
|
+
"schema_version",
|
|
26534
|
+
"company_stage",
|
|
26535
|
+
"icp",
|
|
26536
|
+
"industry",
|
|
26537
|
+
"team_size",
|
|
26538
|
+
"context",
|
|
26539
|
+
"founder_name",
|
|
26540
|
+
"founder_email",
|
|
26541
|
+
"advisor_name",
|
|
26542
|
+
"advisor_email"
|
|
26543
|
+
];
|
|
26544
|
+
function isRecord(value) {
|
|
26545
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26546
|
+
}
|
|
26547
|
+
function hasValidUniqueJsonKeys(source) {
|
|
26548
|
+
let offset = 0;
|
|
26549
|
+
const skipWhitespace = () => {
|
|
26550
|
+
while (/\s/.test(source[offset] ?? "")) offset += 1;
|
|
26551
|
+
};
|
|
26552
|
+
const parseString = () => {
|
|
26553
|
+
if (source[offset] !== '"') return null;
|
|
26554
|
+
const start = offset;
|
|
26555
|
+
offset += 1;
|
|
26556
|
+
let escaped = false;
|
|
26557
|
+
while (offset < source.length) {
|
|
26558
|
+
const char = source[offset];
|
|
26559
|
+
if (escaped) {
|
|
26560
|
+
escaped = false;
|
|
26561
|
+
offset += 1;
|
|
26562
|
+
continue;
|
|
26563
|
+
}
|
|
26564
|
+
if (char === "\\") {
|
|
26565
|
+
escaped = true;
|
|
26566
|
+
offset += 1;
|
|
26567
|
+
continue;
|
|
26568
|
+
}
|
|
26569
|
+
if (char === '"') {
|
|
26570
|
+
offset += 1;
|
|
26571
|
+
try {
|
|
26572
|
+
const parsed = JSON.parse(source.slice(start, offset));
|
|
26573
|
+
return typeof parsed === "string" ? parsed : null;
|
|
26574
|
+
} catch {
|
|
26575
|
+
return null;
|
|
26576
|
+
}
|
|
26577
|
+
}
|
|
26578
|
+
offset += 1;
|
|
26579
|
+
}
|
|
26580
|
+
return null;
|
|
26581
|
+
};
|
|
26582
|
+
const parseValue = (depth) => {
|
|
26583
|
+
if (depth > 32) return false;
|
|
26584
|
+
skipWhitespace();
|
|
26585
|
+
const char = source[offset];
|
|
26586
|
+
if (char === '"') return parseString() !== null;
|
|
26587
|
+
if (char === "{") {
|
|
26588
|
+
offset += 1;
|
|
26589
|
+
skipWhitespace();
|
|
26590
|
+
const keys = /* @__PURE__ */ new Set();
|
|
26591
|
+
if (source[offset] === "}") {
|
|
26592
|
+
offset += 1;
|
|
26593
|
+
return true;
|
|
26594
|
+
}
|
|
26595
|
+
for (; ; ) {
|
|
26596
|
+
skipWhitespace();
|
|
26597
|
+
const key2 = parseString();
|
|
26598
|
+
if (key2 === null) return false;
|
|
26599
|
+
if (keys.has(key2)) return false;
|
|
26600
|
+
keys.add(key2);
|
|
26601
|
+
skipWhitespace();
|
|
26602
|
+
if (source[offset] !== ":") return false;
|
|
26603
|
+
offset += 1;
|
|
26604
|
+
if (!parseValue(depth + 1)) return false;
|
|
26605
|
+
skipWhitespace();
|
|
26606
|
+
if (source[offset] === "}") {
|
|
26607
|
+
offset += 1;
|
|
26608
|
+
return true;
|
|
26609
|
+
}
|
|
26610
|
+
if (source[offset] !== ",") return false;
|
|
26611
|
+
offset += 1;
|
|
26612
|
+
}
|
|
26613
|
+
}
|
|
26614
|
+
if (char === "[") {
|
|
26615
|
+
offset += 1;
|
|
26616
|
+
skipWhitespace();
|
|
26617
|
+
if (source[offset] === "]") {
|
|
26618
|
+
offset += 1;
|
|
26619
|
+
return true;
|
|
26620
|
+
}
|
|
26621
|
+
for (; ; ) {
|
|
26622
|
+
if (!parseValue(depth + 1)) return false;
|
|
26623
|
+
skipWhitespace();
|
|
26624
|
+
if (source[offset] === "]") {
|
|
26625
|
+
offset += 1;
|
|
26626
|
+
return true;
|
|
26627
|
+
}
|
|
26628
|
+
if (source[offset] !== ",") return false;
|
|
26629
|
+
offset += 1;
|
|
26630
|
+
}
|
|
26631
|
+
}
|
|
26632
|
+
const primitiveStart = offset;
|
|
26633
|
+
while (offset < source.length && !/[\s,}\]]/.test(source[offset] ?? "")) offset += 1;
|
|
26634
|
+
return offset > primitiveStart;
|
|
26635
|
+
};
|
|
26636
|
+
if (!parseValue(0)) return false;
|
|
26637
|
+
skipWhitespace();
|
|
26638
|
+
if (offset !== source.length) return false;
|
|
26639
|
+
try {
|
|
26640
|
+
JSON.parse(source);
|
|
26641
|
+
return true;
|
|
26642
|
+
} catch {
|
|
26643
|
+
return false;
|
|
26644
|
+
}
|
|
26645
|
+
}
|
|
26646
|
+
function canonicalBlock(value) {
|
|
26647
|
+
if (!isRecord(value)) return null;
|
|
26648
|
+
const keys = Object.keys(value).sort();
|
|
26649
|
+
const expected = [...ONBOARDING_BLOCK_KEYS].sort();
|
|
26650
|
+
if (keys.length !== expected.length || keys.some((key2, index) => key2 !== expected[index])) return null;
|
|
26651
|
+
if (value.schema_version !== "2") return null;
|
|
26652
|
+
for (const key2 of ONBOARDING_BLOCK_KEYS) {
|
|
26653
|
+
if (typeof value[key2] !== "string") return null;
|
|
26654
|
+
}
|
|
26655
|
+
return value;
|
|
26656
|
+
}
|
|
26657
|
+
function parseOnboardingArtifact(machineText) {
|
|
26658
|
+
const fencePattern = /^```([^\r\n]*)\r?\n([\s\S]*?)^```[ \t]*(?=\r?$)/gm;
|
|
26659
|
+
const fences = [...machineText.matchAll(fencePattern)];
|
|
26660
|
+
const jsonFenceStarts = [...machineText.matchAll(/^```[ \t]*json[ \t]*\r?$/gm)];
|
|
26661
|
+
const matchedJsonFences = fences.filter((match) => match[1].trim() === "json");
|
|
26662
|
+
if (jsonFenceStarts.length !== matchedJsonFences.length) return null;
|
|
26663
|
+
if (fences.some((match) => match[1].trim() !== "json" && /"m8t_onboarding"\s*:/.test(match[2]))) return null;
|
|
26664
|
+
let outsideFences = "";
|
|
26665
|
+
let previousEnd = 0;
|
|
26666
|
+
for (const fence of fences) {
|
|
26667
|
+
const start = fence.index;
|
|
26668
|
+
outsideFences += machineText.slice(previousEnd, start);
|
|
26669
|
+
previousEnd = start + fence[0].length;
|
|
26670
|
+
}
|
|
26671
|
+
outsideFences += machineText.slice(previousEnd);
|
|
26672
|
+
if (/"m8t_onboarding"\s*:/.test(outsideFences)) return null;
|
|
26673
|
+
const artifacts = [];
|
|
26674
|
+
for (const fence of matchedJsonFences) {
|
|
26675
|
+
const json = fence[2].trim();
|
|
26676
|
+
let parsed;
|
|
26677
|
+
if (!hasValidUniqueJsonKeys(json)) {
|
|
26678
|
+
if (json.includes("m8t_onboarding")) return null;
|
|
26679
|
+
continue;
|
|
26680
|
+
}
|
|
26681
|
+
try {
|
|
26682
|
+
parsed = JSON.parse(json);
|
|
26683
|
+
} catch {
|
|
26684
|
+
if (json.includes("m8t_onboarding")) return null;
|
|
26685
|
+
continue;
|
|
26686
|
+
}
|
|
26687
|
+
if (!isRecord(parsed) || !Object.hasOwn(parsed, "m8t_onboarding")) continue;
|
|
26688
|
+
if (Object.keys(parsed).length !== 1) return null;
|
|
26689
|
+
const block = canonicalBlock(parsed.m8t_onboarding);
|
|
26690
|
+
if (!block) return null;
|
|
26691
|
+
const start = fence.index;
|
|
26692
|
+
artifacts.push({ block, start, end: start + fence[0].length });
|
|
26693
|
+
}
|
|
26694
|
+
if (artifacts.length !== 1) return null;
|
|
26695
|
+
const artifact = artifacts[0];
|
|
26696
|
+
const before = machineText.slice(0, artifact.start).trimEnd();
|
|
26697
|
+
const after = machineText.slice(artifact.end).trimStart();
|
|
26698
|
+
return {
|
|
26699
|
+
block: artifact.block,
|
|
26700
|
+
machineText,
|
|
26701
|
+
speechText: [before, after].filter((part) => part.length > 0).join("\n").trim()
|
|
26702
|
+
};
|
|
26703
|
+
}
|
|
26704
|
+
var EMPTY_PROFILE_RESULT = {
|
|
26705
|
+
hadIntake: false,
|
|
26706
|
+
block: null,
|
|
26707
|
+
machineText: null,
|
|
26708
|
+
speechText: null
|
|
26709
|
+
};
|
|
26710
|
+
async function readCursorPages(args) {
|
|
26711
|
+
const all = [];
|
|
26712
|
+
const cursors = /* @__PURE__ */ new Set();
|
|
26713
|
+
let url = args.url;
|
|
26714
|
+
for (let pageNumber = 0; pageNumber < 100; pageNumber += 1) {
|
|
26715
|
+
let response;
|
|
26716
|
+
try {
|
|
26717
|
+
response = await args.fetchImpl(url, { headers: args.headers });
|
|
26718
|
+
} catch {
|
|
26719
|
+
return null;
|
|
26720
|
+
}
|
|
26721
|
+
if (!response.ok) return null;
|
|
26722
|
+
let body;
|
|
26432
26723
|
try {
|
|
26433
|
-
|
|
26434
|
-
if (o?.company_stage && o.context) return o;
|
|
26724
|
+
body = JSON.parse(await response.text());
|
|
26435
26725
|
} catch {
|
|
26726
|
+
return null;
|
|
26727
|
+
}
|
|
26728
|
+
if (!isRecord(body) || !Array.isArray(body.data)) return null;
|
|
26729
|
+
const page = body;
|
|
26730
|
+
all.push(...page.data);
|
|
26731
|
+
if (page.has_more !== true) {
|
|
26732
|
+
if (page.has_more === void 0 || page.has_more === false) return all;
|
|
26733
|
+
return null;
|
|
26436
26734
|
}
|
|
26735
|
+
if (typeof page.last_id !== "string" || page.last_id.trim().length === 0) return null;
|
|
26736
|
+
if (cursors.has(page.last_id)) return null;
|
|
26737
|
+
cursors.add(page.last_id);
|
|
26738
|
+
const next = new URL(url);
|
|
26739
|
+
next.searchParams.set("after", page.last_id);
|
|
26740
|
+
url = next.toString();
|
|
26437
26741
|
}
|
|
26438
26742
|
return null;
|
|
26439
26743
|
}
|
|
26440
|
-
|
|
26744
|
+
function decodeFoundryUser(token) {
|
|
26745
|
+
const parts = token.split(".");
|
|
26746
|
+
if (parts.length !== 3 || parts.some((part) => part.length === 0) || !/^[A-Za-z0-9_-]+$/.test(parts[1] ?? "")) return null;
|
|
26441
26747
|
try {
|
|
26442
|
-
const
|
|
26443
|
-
|
|
26748
|
+
const payload = JSON.parse(Buffer.from(parts[1] ?? "", "base64url").toString("utf8"));
|
|
26749
|
+
if (!isRecord(payload)) return null;
|
|
26750
|
+
const claim = payload.upn ?? payload.preferred_username ?? payload.email;
|
|
26751
|
+
return typeof claim === "string" && claim.trim().length > 0 ? claim : null;
|
|
26444
26752
|
} catch {
|
|
26445
26753
|
return null;
|
|
26446
26754
|
}
|
|
26447
26755
|
}
|
|
26756
|
+
function timestamp(value) {
|
|
26757
|
+
if (typeof value === "number" && Number.isFinite(value)) return value < 1e12 ? value * 1e3 : value;
|
|
26758
|
+
if (typeof value === "string") {
|
|
26759
|
+
const parsed = Date.parse(value);
|
|
26760
|
+
return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
|
|
26761
|
+
}
|
|
26762
|
+
return Number.NEGATIVE_INFINITY;
|
|
26763
|
+
}
|
|
26764
|
+
function orderNewest(values) {
|
|
26765
|
+
if (!values.every((value) => Number.isFinite(value.createdAt))) return values;
|
|
26766
|
+
return values.sort((a, b) => b.createdAt - a.createdAt || a.ordinal - b.ordinal || b.id.localeCompare(a.id));
|
|
26767
|
+
}
|
|
26448
26768
|
async function findOnboardingProfile(args) {
|
|
26769
|
+
const userId = decodeFoundryUser(args.token);
|
|
26770
|
+
if (!userId) return { ...EMPTY_PROFILE_RESULT };
|
|
26449
26771
|
const doFetch = args.fetchImpl ?? fetch;
|
|
26450
26772
|
const H = { Authorization: `Bearer ${args.token}` };
|
|
26451
|
-
const
|
|
26452
|
-
const
|
|
26453
|
-
|
|
26454
|
-
|
|
26455
|
-
|
|
26456
|
-
|
|
26457
|
-
|
|
26458
|
-
|
|
26459
|
-
if (
|
|
26460
|
-
|
|
26461
|
-
|
|
26773
|
+
const base = args.endpoint.replace(/\/+$/, "");
|
|
26774
|
+
const listed = await readCursorPages({
|
|
26775
|
+
url: `${base}/openai/v1/conversations?order=desc&limit=100`,
|
|
26776
|
+
headers: H,
|
|
26777
|
+
fetchImpl: doFetch
|
|
26778
|
+
});
|
|
26779
|
+
if (!listed) return { ...EMPTY_PROFILE_RESULT };
|
|
26780
|
+
const conversations = orderNewest(listed.flatMap((value, ordinal) => {
|
|
26781
|
+
if (!isRecord(value) || typeof value.id !== "string" || value.id.length === 0 || !isRecord(value.metadata)) return [];
|
|
26782
|
+
const metadata = value.metadata;
|
|
26783
|
+
if (metadata.app !== "m8t-webapp" || metadata.agent !== "stacey-intake" || metadata.userId !== userId) return [];
|
|
26784
|
+
return [{
|
|
26785
|
+
id: value.id,
|
|
26786
|
+
createdAt: timestamp(value.created_at) !== Number.NEGATIVE_INFINITY ? timestamp(value.created_at) : timestamp(metadata.createdAt),
|
|
26787
|
+
ordinal
|
|
26788
|
+
}];
|
|
26789
|
+
}));
|
|
26790
|
+
const conversation = conversations.at(0);
|
|
26791
|
+
if (conversation === void 0) return { ...EMPTY_PROFILE_RESULT };
|
|
26792
|
+
const items = await readCursorPages({
|
|
26793
|
+
url: `${base}/openai/v1/conversations/${encodeURIComponent(conversation.id)}/items?order=desc&limit=100`,
|
|
26794
|
+
headers: H,
|
|
26795
|
+
fetchImpl: doFetch
|
|
26796
|
+
});
|
|
26797
|
+
if (!items) return { hadIntake: true, block: null, machineText: null, speechText: null };
|
|
26798
|
+
const assistantItems = orderNewest(items.flatMap((value, ordinal) => {
|
|
26799
|
+
if (!isRecord(value) || value.type !== "message" || value.role !== "assistant" || !Array.isArray(value.content)) return [];
|
|
26800
|
+
const id = typeof value.id === "string" ? value.id : "";
|
|
26801
|
+
return [{ value, id, createdAt: timestamp(value.created_at), ordinal }];
|
|
26802
|
+
}));
|
|
26803
|
+
const artifacts = [];
|
|
26804
|
+
let malformedArtifact = false;
|
|
26805
|
+
for (const item of assistantItems) {
|
|
26806
|
+
const content = item.value.content;
|
|
26807
|
+
const machineText = content.flatMap((part) => {
|
|
26808
|
+
if (!isRecord(part)) return [];
|
|
26809
|
+
if (typeof part.text === "string" && part.text.length > 0) return [part.text];
|
|
26810
|
+
if (typeof part.transcript === "string") return [part.transcript];
|
|
26811
|
+
return [];
|
|
26812
|
+
}).join("\n");
|
|
26813
|
+
const artifact2 = parseOnboardingArtifact(machineText);
|
|
26814
|
+
if (artifact2) artifacts.push(artifact2);
|
|
26815
|
+
else if (machineText.includes("m8t_onboarding")) malformedArtifact = true;
|
|
26816
|
+
}
|
|
26817
|
+
if (malformedArtifact || artifacts.length !== 1) {
|
|
26818
|
+
return { hadIntake: true, block: null, machineText: null, speechText: null };
|
|
26819
|
+
}
|
|
26820
|
+
const artifact = artifacts[0];
|
|
26821
|
+
return {
|
|
26822
|
+
hadIntake: true,
|
|
26823
|
+
block: artifact.block,
|
|
26824
|
+
machineText: artifact.machineText,
|
|
26825
|
+
speechText: artifact.speechText
|
|
26826
|
+
};
|
|
26462
26827
|
}
|
|
26463
26828
|
function renderCompanyProfile(block, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
26464
26829
|
const dash = (v) => v?.trim() ? v.trim() : "\u2014";
|
|
@@ -26490,24 +26855,6 @@ function renderCompanyProfile(block, now = (/* @__PURE__ */ new Date()).toISOStr
|
|
|
26490
26855
|
const memoryIndexLine = `- \`${COMPANY_PROFILE_PATH}\` \u2014 **Company profile**: ${bits}. (seeded from onboarding)`;
|
|
26491
26856
|
return { profileMd, memoryIndexLine };
|
|
26492
26857
|
}
|
|
26493
|
-
function companyProfileBodyEquals(a, b) {
|
|
26494
|
-
const strip = (s) => s.replace(/^(created|updated):.*$/gm, "").trim();
|
|
26495
|
-
return strip(a) === strip(b);
|
|
26496
|
-
}
|
|
26497
|
-
function upsertMemoryIndex(existing, line2, targetPath2 = COMPANY_PROFILE_PATH) {
|
|
26498
|
-
const lines = existing.split("\n");
|
|
26499
|
-
const existingIdx = lines.findIndex((l) => l.includes(`\`${targetPath2}\``));
|
|
26500
|
-
if (existingIdx >= 0) {
|
|
26501
|
-
lines[existingIdx] = line2;
|
|
26502
|
-
return lines.join("\n");
|
|
26503
|
-
}
|
|
26504
|
-
const firstItemIdx = lines.findIndex((l) => /^-\s+`memory\//.test(l));
|
|
26505
|
-
if (firstItemIdx >= 0) {
|
|
26506
|
-
lines.splice(firstItemIdx, 0, line2);
|
|
26507
|
-
return lines.join("\n");
|
|
26508
|
-
}
|
|
26509
|
-
return existing.replace(/\s*$/, "\n\n") + line2 + "\n";
|
|
26510
|
-
}
|
|
26511
26858
|
var FOUNDER_RECORD_PATH = "memory/founder.md";
|
|
26512
26859
|
var NOT_CAPTURED = "_not captured yet \u2014 add it anytime (run `m8t bootstrap seed-profile`, or just tell me)_";
|
|
26513
26860
|
function renderFounderRecord(block, inputs, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
@@ -26545,10 +26892,6 @@ function renderFounderRecord(block, inputs, now = (/* @__PURE__ */ new Date()).t
|
|
|
26545
26892
|
const memoryIndexLine = `- \`${FOUNDER_RECORD_PATH}\` \u2014 **Founder & install context**: ${founderName || "founder"} \xB7 advisor ${idxAdvisor} \xB7 sub ${idxSub}. (seeded from onboarding)`;
|
|
26546
26893
|
return { founderMd, memoryIndexLine };
|
|
26547
26894
|
}
|
|
26548
|
-
function founderRecordBodyEquals(a, b) {
|
|
26549
|
-
const strip = (s) => s.replace(/^(created|updated):.*$/gm, "").trim();
|
|
26550
|
-
return strip(a) === strip(b);
|
|
26551
|
-
}
|
|
26552
26895
|
|
|
26553
26896
|
// src/lib/founder-identity.ts
|
|
26554
26897
|
function deriveEmailCandidate(raw) {
|
|
@@ -26620,6 +26963,44 @@ async function resolveSeedContext(opts) {
|
|
|
26620
26963
|
const brainRepos = opts.brainOverride ? [opts.brainOverride] : [`${appCreds.org}/stacey-brain`, `${appCreds.org}/azzy-brain`];
|
|
26621
26964
|
return { endpoint, brainRepos, appCreds, subscriptionId };
|
|
26622
26965
|
}
|
|
26966
|
+
var MEMORY_INDEX_PATH = "memory/MEMORY.md";
|
|
26967
|
+
function normalizeFrontmatterTimestamps(markdown) {
|
|
26968
|
+
const lines = markdown.split("\n");
|
|
26969
|
+
if (lines[0]?.replace(/\r$/, "") !== "---") return markdown;
|
|
26970
|
+
const end = lines.findIndex((line2, index) => index > 0 && line2.replace(/\r$/, "") === "---");
|
|
26971
|
+
if (end < 0) return markdown;
|
|
26972
|
+
return lines.map((line2, index) => {
|
|
26973
|
+
if (index <= 0 || index >= end) return line2;
|
|
26974
|
+
return /^(created|updated):/.test(line2) ? line2.replace(/^(created|updated):.*$/, "$1: <generated>") : line2;
|
|
26975
|
+
}).join("\n");
|
|
26976
|
+
}
|
|
26977
|
+
function seededDocumentMatches(actual, expected) {
|
|
26978
|
+
return actual != null && normalizeFrontmatterTimestamps(actual) === normalizeFrontmatterTimestamps(expected);
|
|
26979
|
+
}
|
|
26980
|
+
function upsertMemoryIndexOnce(existing, line2, targetPath2) {
|
|
26981
|
+
const lines = existing.split("\n");
|
|
26982
|
+
const kept = [];
|
|
26983
|
+
let insertionIndex;
|
|
26984
|
+
for (const existingLine of lines) {
|
|
26985
|
+
if (existingLine.includes(`\`${targetPath2}\``)) {
|
|
26986
|
+
insertionIndex ??= kept.length;
|
|
26987
|
+
} else {
|
|
26988
|
+
kept.push(existingLine);
|
|
26989
|
+
}
|
|
26990
|
+
}
|
|
26991
|
+
if (insertionIndex === void 0) {
|
|
26992
|
+
const firstMemoryItem = kept.findIndex((existingLine) => /^-\s+`memory\//.test(existingLine));
|
|
26993
|
+
if (firstMemoryItem >= 0) {
|
|
26994
|
+
insertionIndex = firstMemoryItem;
|
|
26995
|
+
} else {
|
|
26996
|
+
const base = kept.join("\n").replace(/\s*$/, "\n\n");
|
|
26997
|
+
return `${base}${line2}
|
|
26998
|
+
`;
|
|
26999
|
+
}
|
|
27000
|
+
}
|
|
27001
|
+
kept.splice(insertionIndex, 0, line2);
|
|
27002
|
+
return kept.join("\n");
|
|
27003
|
+
}
|
|
26623
27004
|
async function applyProfileToBrain(args) {
|
|
26624
27005
|
const token = await mintInstallationTokenFromPem({
|
|
26625
27006
|
appId: args.appCreds.appId,
|
|
@@ -26633,29 +27014,84 @@ async function applyProfileToBrain(args) {
|
|
|
26633
27014
|
{ subscriptionId: args.subscriptionId, azIdentity: args.azIdentity },
|
|
26634
27015
|
args.now
|
|
26635
27016
|
);
|
|
26636
|
-
const read = (p) => readRepoFileViaApp({
|
|
26637
|
-
const existingProfile = await read(COMPANY_PROFILE_PATH);
|
|
26638
|
-
const existingFounder = await read(FOUNDER_RECORD_PATH);
|
|
26639
|
-
const existingIndex = await read("memory/MEMORY.md") ?? DEFAULT_MEMORY_INDEX_HEADER;
|
|
26640
|
-
let nextIndex = upsertMemoryIndex(existingIndex, companyLine, COMPANY_PROFILE_PATH);
|
|
26641
|
-
nextIndex = upsertMemoryIndex(nextIndex, founderLine, FOUNDER_RECORD_PATH);
|
|
26642
|
-
const profileSame = existingProfile != null && companyProfileBodyEquals(existingProfile, profileMd);
|
|
26643
|
-
const founderSame = existingFounder != null && founderRecordBodyEquals(existingFounder, founderMd);
|
|
26644
|
-
if (profileSame && founderSame && nextIndex === existingIndex) {
|
|
26645
|
-
return;
|
|
26646
|
-
}
|
|
26647
|
-
await commitFilesViaApp({
|
|
27017
|
+
const read = (p) => readRepoFileViaApp({
|
|
26648
27018
|
token,
|
|
26649
27019
|
repo: args.brainRepo,
|
|
26650
|
-
|
|
26651
|
-
|
|
26652
|
-
files: [
|
|
26653
|
-
{ path: COMPANY_PROFILE_PATH, content: profileMd },
|
|
26654
|
-
{ path: FOUNDER_RECORD_PATH, content: founderMd },
|
|
26655
|
-
{ path: "memory/MEMORY.md", content: nextIndex }
|
|
26656
|
-
],
|
|
27020
|
+
path: p,
|
|
27021
|
+
ref: args.branch,
|
|
26657
27022
|
fetchImpl: args.fetchImpl
|
|
26658
27023
|
});
|
|
27024
|
+
const existingProfile = await read(COMPANY_PROFILE_PATH);
|
|
27025
|
+
const existingFounder = await read(FOUNDER_RECORD_PATH);
|
|
27026
|
+
const existingIndex = await read(MEMORY_INDEX_PATH) ?? DEFAULT_MEMORY_INDEX_HEADER;
|
|
27027
|
+
let nextIndex = upsertMemoryIndexOnce(existingIndex, companyLine, COMPANY_PROFILE_PATH);
|
|
27028
|
+
nextIndex = upsertMemoryIndexOnce(nextIndex, founderLine, FOUNDER_RECORD_PATH);
|
|
27029
|
+
const files = [
|
|
27030
|
+
...!seededDocumentMatches(existingProfile, profileMd) ? [{ path: COMPANY_PROFILE_PATH, content: profileMd }] : [],
|
|
27031
|
+
...!seededDocumentMatches(existingFounder, founderMd) ? [{ path: FOUNDER_RECORD_PATH, content: founderMd }] : [],
|
|
27032
|
+
...nextIndex !== existingIndex ? [{ path: MEMORY_INDEX_PATH, content: nextIndex }] : []
|
|
27033
|
+
];
|
|
27034
|
+
if (files.length > 0) {
|
|
27035
|
+
await commitFilesViaApp({
|
|
27036
|
+
token,
|
|
27037
|
+
repo: args.brainRepo,
|
|
27038
|
+
branch: args.branch,
|
|
27039
|
+
message: "seed(brain): founder + company profile from onboarding",
|
|
27040
|
+
files,
|
|
27041
|
+
fetchImpl: args.fetchImpl
|
|
27042
|
+
});
|
|
27043
|
+
const [verifiedProfile, verifiedFounder, verifiedIndex] = await Promise.all([
|
|
27044
|
+
read(COMPANY_PROFILE_PATH),
|
|
27045
|
+
read(FOUNDER_RECORD_PATH),
|
|
27046
|
+
read(MEMORY_INDEX_PATH)
|
|
27047
|
+
]);
|
|
27048
|
+
const mismatches = [
|
|
27049
|
+
...!seededDocumentMatches(verifiedProfile, profileMd) ? [COMPANY_PROFILE_PATH] : [],
|
|
27050
|
+
...!seededDocumentMatches(verifiedFounder, founderMd) ? [FOUNDER_RECORD_PATH] : [],
|
|
27051
|
+
...verifiedIndex !== nextIndex ? [MEMORY_INDEX_PATH] : []
|
|
27052
|
+
];
|
|
27053
|
+
if (mismatches.length > 0) {
|
|
27054
|
+
throw new LocalCliError({
|
|
27055
|
+
code: "SEED_PROFILE_READBACK_FAILED",
|
|
27056
|
+
message: `Profile write could not be verified in ${args.brainRepo}: ${mismatches.join(", ")}.`,
|
|
27057
|
+
hint: "Re-run 'm8t bootstrap seed-profile'; verified files will be left unchanged."
|
|
27058
|
+
});
|
|
27059
|
+
}
|
|
27060
|
+
}
|
|
27061
|
+
}
|
|
27062
|
+
async function applyProfileToBrains(args) {
|
|
27063
|
+
const brainRepos = [...new Set(args.brainRepos)];
|
|
27064
|
+
if (brainRepos.length === 0) {
|
|
27065
|
+
throw new LocalCliError({
|
|
27066
|
+
code: "SEED_PROFILE_INCOMPLETE",
|
|
27067
|
+
message: "Profile seeding is incomplete: no brain repositories were resolved.",
|
|
27068
|
+
hint: "Re-run 'm8t bootstrap seed-profile' after the Stacey and Azzy brains are provisioned."
|
|
27069
|
+
});
|
|
27070
|
+
}
|
|
27071
|
+
const settled = await Promise.allSettled(brainRepos.map(async (brainRepo) => {
|
|
27072
|
+
await applyProfileToBrain({ ...args, brainRepo });
|
|
27073
|
+
return brainRepo;
|
|
27074
|
+
}));
|
|
27075
|
+
const verified = [];
|
|
27076
|
+
const failed = [];
|
|
27077
|
+
const causes = [];
|
|
27078
|
+
settled.forEach((result, index) => {
|
|
27079
|
+
const brainRepo = brainRepos[index];
|
|
27080
|
+
if (result.status === "fulfilled") {
|
|
27081
|
+
verified.push(brainRepo);
|
|
27082
|
+
} else {
|
|
27083
|
+
failed.push(brainRepo);
|
|
27084
|
+
causes.push(result.reason);
|
|
27085
|
+
}
|
|
27086
|
+
});
|
|
27087
|
+
if (failed.length > 0 || verified.length !== brainRepos.length) {
|
|
27088
|
+
throw new LocalCliError({
|
|
27089
|
+
code: "SEED_PROFILE_INCOMPLETE",
|
|
27090
|
+
message: `Profile seeding is incomplete: verified ${verified.join(", ") || "none"}; failed ${failed.join(", ") || "none"}.`,
|
|
27091
|
+
hint: "Re-run 'm8t bootstrap seed-profile'; verified brain files will be skipped and missing files repaired.",
|
|
27092
|
+
cause: new AggregateError(causes, "One or more brain profile writes failed verification.")
|
|
27093
|
+
});
|
|
27094
|
+
}
|
|
26659
27095
|
}
|
|
26660
27096
|
function spawnDetachedSeedWatch() {
|
|
26661
27097
|
const logPath = path33.join(os15.homedir(), ".m8t", "seed-profile.log");
|
|
@@ -26677,17 +27113,15 @@ async function reactiveSeedOnFinish(args) {
|
|
|
26677
27113
|
const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token, fetchImpl: args.fetchImpl });
|
|
26678
27114
|
if (block) {
|
|
26679
27115
|
const azIdentity = args.azIdentity ?? await (args.getSignedInUserIdentityImpl ?? getSignedInUserIdentity)();
|
|
26680
|
-
|
|
26681
|
-
|
|
26682
|
-
|
|
26683
|
-
|
|
26684
|
-
|
|
26685
|
-
|
|
26686
|
-
|
|
26687
|
-
|
|
26688
|
-
|
|
26689
|
-
});
|
|
26690
|
-
}
|
|
27116
|
+
await applyProfileToBrains({
|
|
27117
|
+
block,
|
|
27118
|
+
brainRepos: ctx.brainRepos,
|
|
27119
|
+
branch: "main",
|
|
27120
|
+
appCreds: ctx.appCreds,
|
|
27121
|
+
subscriptionId: ctx.subscriptionId,
|
|
27122
|
+
azIdentity,
|
|
27123
|
+
fetchImpl: args.fetchImpl
|
|
27124
|
+
});
|
|
26691
27125
|
args.stdout(`${colors.success("\u2713")} Your advisors now know your company + how to reach you (seeded ${ctx.brainRepos.join(", ")}).
|
|
26692
27126
|
`);
|
|
26693
27127
|
return;
|
|
@@ -26854,6 +27288,7 @@ import * as fs32 from "fs";
|
|
|
26854
27288
|
import * as net from "net";
|
|
26855
27289
|
import * as os17 from "os";
|
|
26856
27290
|
import * as path35 from "path";
|
|
27291
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
26857
27292
|
import { spawn as spawn7, spawnSync as spawnSync6 } from "child_process";
|
|
26858
27293
|
init_errors();
|
|
26859
27294
|
init_rbac();
|
|
@@ -27018,9 +27453,19 @@ async function ensureFounderFoundryRole(args) {
|
|
|
27018
27453
|
}
|
|
27019
27454
|
function writeWebEnvLocal(args) {
|
|
27020
27455
|
const envPath = path35.join(args.repoRoot, "apps", "web", ".env.local");
|
|
27456
|
+
let prior = "";
|
|
27021
27457
|
if (fs32.existsSync(envPath)) {
|
|
27458
|
+
prior = fs32.readFileSync(envPath, "utf8");
|
|
27022
27459
|
fs32.copyFileSync(envPath, envPath + ".bak");
|
|
27023
27460
|
}
|
|
27461
|
+
const priorSecret = readValidInternalSecret(prior);
|
|
27462
|
+
const internalSecret = priorSecret ?? (args.randomBytesImpl ?? randomBytes3)(32).toString("base64url");
|
|
27463
|
+
if (!isValidInternalSecret(internalSecret)) {
|
|
27464
|
+
throw new LocalCliError({
|
|
27465
|
+
code: "BOOTSTRAP_UI_INTERNAL_SECRET_GENERATION_FAILED",
|
|
27466
|
+
message: "Could not generate a valid local internal service secret."
|
|
27467
|
+
});
|
|
27468
|
+
}
|
|
27024
27469
|
const body = [
|
|
27025
27470
|
"# Written by `m8t bootstrap ui` \u2014 onboarding chat-only run (Simple Stacey).",
|
|
27026
27471
|
"# STORAGE unset \u2192 gateway boot skipped; the webapp forwards your MSAL token to Foundry.",
|
|
@@ -27029,14 +27474,47 @@ function writeWebEnvLocal(args) {
|
|
|
27029
27474
|
`FOUNDRY_PROJECT_ENDPOINT=${args.foundryEndpoint}`,
|
|
27030
27475
|
"VOICE_RELAY_PUBLIC_URL=ws://localhost:8790",
|
|
27031
27476
|
"STORAGE_ACCOUNT_NAME=",
|
|
27477
|
+
`M8T_INTERNAL_SECRET=${internalSecret}`,
|
|
27478
|
+
"M8T_SINGLE_GATEWAY_PROCESS=1",
|
|
27032
27479
|
"M8T_DISABLE_WORKER=1",
|
|
27033
27480
|
"M8T_ONBOARDING=1",
|
|
27034
27481
|
"NEXT_PUBLIC_M8T_ONBOARDING=1",
|
|
27035
27482
|
""
|
|
27036
27483
|
].join("\n");
|
|
27037
|
-
fs32.writeFileSync(envPath, body, "utf8");
|
|
27484
|
+
fs32.writeFileSync(envPath, body, { encoding: "utf8", mode: 384 });
|
|
27485
|
+
try {
|
|
27486
|
+
fs32.chmodSync(envPath, 384);
|
|
27487
|
+
} catch {
|
|
27488
|
+
}
|
|
27038
27489
|
return envPath;
|
|
27039
27490
|
}
|
|
27491
|
+
function isValidInternalSecret(value) {
|
|
27492
|
+
if (!value || !/^[A-Za-z0-9_-]{43}$/.test(value)) return false;
|
|
27493
|
+
const decoded = Buffer.from(value, "base64url");
|
|
27494
|
+
return decoded.length === 32 && decoded.toString("base64url") === value;
|
|
27495
|
+
}
|
|
27496
|
+
function readValidInternalSecret(envText) {
|
|
27497
|
+
const values = envText.split(/\r?\n/).filter((line2) => line2.startsWith("M8T_INTERNAL_SECRET=")).map((line2) => line2.slice("M8T_INTERNAL_SECRET=".length));
|
|
27498
|
+
if (values.length !== 1) return void 0;
|
|
27499
|
+
return isValidInternalSecret(values[0]) ? values[0] : void 0;
|
|
27500
|
+
}
|
|
27501
|
+
function readWebInternalSecret(repoRoot) {
|
|
27502
|
+
const envPath = path35.join(repoRoot, "apps", "web", ".env.local");
|
|
27503
|
+
let secret;
|
|
27504
|
+
try {
|
|
27505
|
+
secret = readValidInternalSecret(fs32.readFileSync(envPath, "utf8"));
|
|
27506
|
+
} catch {
|
|
27507
|
+
secret = void 0;
|
|
27508
|
+
}
|
|
27509
|
+
if (!secret) {
|
|
27510
|
+
throw new LocalCliError({
|
|
27511
|
+
code: "BOOTSTRAP_UI_INTERNAL_SECRET_MISSING",
|
|
27512
|
+
message: "The local web environment has no valid internal service secret.",
|
|
27513
|
+
hint: "Re-run 'm8t bootstrap ui' so the web app and voice relay receive the same secret."
|
|
27514
|
+
});
|
|
27515
|
+
}
|
|
27516
|
+
return secret;
|
|
27517
|
+
}
|
|
27040
27518
|
function assertNodeVersion(versionString = process.version) {
|
|
27041
27519
|
const major = Number(/^v?(\d+)\./.exec(versionString)?.[1] ?? "0");
|
|
27042
27520
|
if (major < 20) {
|
|
@@ -27106,20 +27584,67 @@ function isLocalPortOpen(port) {
|
|
|
27106
27584
|
});
|
|
27107
27585
|
});
|
|
27108
27586
|
}
|
|
27587
|
+
function removePidFile(pidPath) {
|
|
27588
|
+
try {
|
|
27589
|
+
fs32.unlinkSync(pidPath);
|
|
27590
|
+
} catch {
|
|
27591
|
+
}
|
|
27592
|
+
}
|
|
27593
|
+
function parseManagedPid(text) {
|
|
27594
|
+
if (!/^[1-9]\d*$/.test(text)) return null;
|
|
27595
|
+
const pid = Number(text);
|
|
27596
|
+
if (!Number.isSafeInteger(pid) || pid < 2 || pid === process.pid || pid === process.ppid) return null;
|
|
27597
|
+
return pid;
|
|
27598
|
+
}
|
|
27599
|
+
function readLiveManagedPid(pidPath) {
|
|
27600
|
+
let text;
|
|
27601
|
+
try {
|
|
27602
|
+
text = fs32.readFileSync(pidPath, "utf8").trim();
|
|
27603
|
+
} catch {
|
|
27604
|
+
return null;
|
|
27605
|
+
}
|
|
27606
|
+
const pid = parseManagedPid(text);
|
|
27607
|
+
if (pid == null) {
|
|
27608
|
+
removePidFile(pidPath);
|
|
27609
|
+
return null;
|
|
27610
|
+
}
|
|
27611
|
+
try {
|
|
27612
|
+
process.kill(pid, 0);
|
|
27613
|
+
return pid;
|
|
27614
|
+
} catch (error) {
|
|
27615
|
+
const code = error.code;
|
|
27616
|
+
if (code === "ESRCH") {
|
|
27617
|
+
removePidFile(pidPath);
|
|
27618
|
+
return null;
|
|
27619
|
+
}
|
|
27620
|
+
if (code === "EPERM") return pid;
|
|
27621
|
+
throw error;
|
|
27622
|
+
}
|
|
27623
|
+
}
|
|
27109
27624
|
async function serveOnboardingUiDetached(args) {
|
|
27110
27625
|
const { logPath, pidPath } = onboardingUiPaths();
|
|
27111
27626
|
const portNum = Number(args.port);
|
|
27112
27627
|
const isPortOpen = () => isLocalPortOpen(portNum);
|
|
27113
|
-
|
|
27628
|
+
const portOpen = await isPortOpen();
|
|
27629
|
+
const managedPid = readLiveManagedPid(pidPath);
|
|
27630
|
+
if (portOpen && managedPid != null) {
|
|
27114
27631
|
return { alreadyRunning: true, logPath };
|
|
27115
27632
|
}
|
|
27633
|
+
if (portOpen) {
|
|
27634
|
+
throw new LocalCliError({
|
|
27635
|
+
code: "BOOTSTRAP_UI_PORT_IN_USE",
|
|
27636
|
+
message: `Port ${args.port} is occupied by a process not started by m8t bootstrap.`,
|
|
27637
|
+
hint: `Stop the process using port ${args.port}, then re-run 'm8t bootstrap ui'.`
|
|
27638
|
+
});
|
|
27639
|
+
}
|
|
27640
|
+
if (managedPid != null) return { alreadyRunning: true, logPath };
|
|
27116
27641
|
fs32.mkdirSync(path35.dirname(logPath), { recursive: true });
|
|
27117
27642
|
const fd = fs32.openSync(logPath, "a");
|
|
27118
27643
|
const child = spawn7("pnpm", ["--filter", "web", "dev"], {
|
|
27119
27644
|
cwd: args.repoRoot,
|
|
27120
27645
|
detached: true,
|
|
27121
27646
|
stdio: ["ignore", fd, fd],
|
|
27122
|
-
env: { ...process.env, PORT: args.port },
|
|
27647
|
+
env: { ...process.env, PORT: args.port, M8T_SINGLE_GATEWAY_PROCESS: "1" },
|
|
27123
27648
|
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
27124
27649
|
shell: process.platform === "win32"
|
|
27125
27650
|
});
|
|
@@ -27144,9 +27669,26 @@ async function serveOnboardingUiDetached(args) {
|
|
|
27144
27669
|
async function serveOnboardingRelayDetached(args) {
|
|
27145
27670
|
const { logPath, pidPath } = onboardingRelayPaths();
|
|
27146
27671
|
const portNum = 8790;
|
|
27147
|
-
if (
|
|
27672
|
+
if (args.webInternalBaseUrl !== "http://localhost:3000") {
|
|
27673
|
+
throw new LocalCliError({
|
|
27674
|
+
code: "BOOTSTRAP_UI_INTERNAL_BASE_URL_INVALID",
|
|
27675
|
+
message: "The local voice relay internal base URL must be http://localhost:3000."
|
|
27676
|
+
});
|
|
27677
|
+
}
|
|
27678
|
+
const internalSecret = readWebInternalSecret(args.repoRoot);
|
|
27679
|
+
const portOpen = await isLocalPortOpen(portNum);
|
|
27680
|
+
const managedPid = readLiveManagedPid(pidPath);
|
|
27681
|
+
if (portOpen && managedPid != null) {
|
|
27148
27682
|
return { alreadyRunning: true, logPath };
|
|
27149
27683
|
}
|
|
27684
|
+
if (portOpen) {
|
|
27685
|
+
throw new LocalCliError({
|
|
27686
|
+
code: "BOOTSTRAP_UI_RELAY_PORT_IN_USE",
|
|
27687
|
+
message: "Port 8790 is occupied by a process not started by m8t bootstrap.",
|
|
27688
|
+
hint: "Stop the process using port 8790, then re-run 'm8t bootstrap ui'."
|
|
27689
|
+
});
|
|
27690
|
+
}
|
|
27691
|
+
if (managedPid != null) return { alreadyRunning: true, logPath };
|
|
27150
27692
|
fs32.mkdirSync(path35.dirname(logPath), { recursive: true });
|
|
27151
27693
|
const fd = fs32.openSync(logPath, "a");
|
|
27152
27694
|
const child = spawn7("pnpm", ["--filter", "web", "exec", "tsx", "voice-relay-entry.ts"], {
|
|
@@ -27159,7 +27701,9 @@ async function serveOnboardingRelayDetached(args) {
|
|
|
27159
27701
|
AZURE_TENANT_ID: args.tenantId,
|
|
27160
27702
|
AZURE_CLIENT_ID: args.clientId,
|
|
27161
27703
|
RELAY_ALLOWED_ORIGINS: "http://localhost:3000",
|
|
27162
|
-
RELAY_PORT: "8790"
|
|
27704
|
+
RELAY_PORT: "8790",
|
|
27705
|
+
M8T_INTERNAL_SECRET: internalSecret,
|
|
27706
|
+
WEB_INTERNAL_BASE_URL: args.webInternalBaseUrl
|
|
27163
27707
|
},
|
|
27164
27708
|
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
27165
27709
|
shell: process.platform === "win32"
|
|
@@ -27199,8 +27743,8 @@ function stopPidFile(pidPath) {
|
|
|
27199
27743
|
} catch {
|
|
27200
27744
|
return false;
|
|
27201
27745
|
}
|
|
27202
|
-
const pid =
|
|
27203
|
-
if (
|
|
27746
|
+
const pid = parseManagedPid(pidStr);
|
|
27747
|
+
if (pid != null) {
|
|
27204
27748
|
if (process.platform === "win32") {
|
|
27205
27749
|
spawnSync6("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
|
|
27206
27750
|
} else {
|
|
@@ -27353,7 +27897,8 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
27353
27897
|
repoRoot,
|
|
27354
27898
|
foundryEndpoint: endpoint,
|
|
27355
27899
|
tenantId: account.tenantId,
|
|
27356
|
-
clientId: state.appRegClientId
|
|
27900
|
+
clientId: state.appRegClientId,
|
|
27901
|
+
webInternalBaseUrl: "http://localhost:3000"
|
|
27357
27902
|
});
|
|
27358
27903
|
out("voice relay on :8790");
|
|
27359
27904
|
const outcome2 = await deployOutcome;
|
|
@@ -27371,7 +27916,8 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
27371
27916
|
repoRoot,
|
|
27372
27917
|
foundryEndpoint: endpoint,
|
|
27373
27918
|
tenantId: account.tenantId,
|
|
27374
|
-
clientId: state.appRegClientId
|
|
27919
|
+
clientId: state.appRegClientId,
|
|
27920
|
+
webInternalBaseUrl: "http://localhost:3000"
|
|
27375
27921
|
});
|
|
27376
27922
|
out("voice relay on :8790");
|
|
27377
27923
|
out("starting the webapp on :3000 in the background\u2026");
|
|
@@ -27438,9 +27984,14 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
27438
27984
|
const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token });
|
|
27439
27985
|
if (block) {
|
|
27440
27986
|
const azIdentity = await getSignedInUserIdentity();
|
|
27441
|
-
|
|
27442
|
-
|
|
27443
|
-
|
|
27987
|
+
await applyProfileToBrains({
|
|
27988
|
+
block,
|
|
27989
|
+
brainRepos: ctx.brainRepos,
|
|
27990
|
+
branch: "main",
|
|
27991
|
+
appCreds: ctx.appCreds,
|
|
27992
|
+
subscriptionId: ctx.subscriptionId,
|
|
27993
|
+
azIdentity
|
|
27994
|
+
});
|
|
27444
27995
|
this.context.stdout.write(`${colors.success("\u2713")} seeded your advisors' brains (${ctx.brainRepos.join(", ")}) from your questionnaire.
|
|
27445
27996
|
`);
|
|
27446
27997
|
return 0;
|