@m8t-stack/cli 0.2.11 → 0.2.13
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 +250 -79
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1195,7 +1195,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1195
1195
|
import { Builtins, Cli } from "clipanion";
|
|
1196
1196
|
|
|
1197
1197
|
// src/lib/package-version.ts
|
|
1198
|
-
var CLI_VERSION = "0.2.
|
|
1198
|
+
var CLI_VERSION = "0.2.13";
|
|
1199
1199
|
|
|
1200
1200
|
// src/lib/render-error.ts
|
|
1201
1201
|
init_errors();
|
|
@@ -15064,10 +15064,9 @@ async function deployPromptAdvisor(args) {
|
|
|
15064
15064
|
const fillableFields = fm["fillable-fields"] ?? [];
|
|
15065
15065
|
const valuesMap = {};
|
|
15066
15066
|
for (const field of fillableFields) {
|
|
15067
|
-
if (field.default !== void 0)
|
|
15068
|
-
valuesMap[field.name] = field.default;
|
|
15069
|
-
}
|
|
15067
|
+
if (field.default !== void 0) valuesMap[field.name] = field.default;
|
|
15070
15068
|
}
|
|
15069
|
+
for (const [k, v] of Object.entries(args.fieldOverrides ?? {})) valuesMap[k] = v;
|
|
15071
15070
|
const instructions = renderPersonaBody(personaPath, valuesMap);
|
|
15072
15071
|
return createPromptVersion({
|
|
15073
15072
|
credential: args.credential,
|
|
@@ -15196,6 +15195,109 @@ init_errors();
|
|
|
15196
15195
|
init_foundry_agent_get();
|
|
15197
15196
|
import { randomBytes as randomBytes2, createHash } from "crypto";
|
|
15198
15197
|
|
|
15198
|
+
// src/lib/data-plane-ready.ts
|
|
15199
|
+
init_errors();
|
|
15200
|
+
async function awaitDataPlaneReady(opts) {
|
|
15201
|
+
const consecutive = opts.consecutive ?? 3;
|
|
15202
|
+
const attempts = opts.attempts ?? 60;
|
|
15203
|
+
const intervalMs = opts.intervalMs ?? 5e3;
|
|
15204
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15205
|
+
let streak = 0;
|
|
15206
|
+
for (let i = 1; i <= attempts; i++) {
|
|
15207
|
+
const r = await opts.probe();
|
|
15208
|
+
if (r.ok) {
|
|
15209
|
+
streak++;
|
|
15210
|
+
opts.onProgress?.(`data-plane ok (${streak.toString()}/${consecutive.toString()})`);
|
|
15211
|
+
if (streak >= consecutive) return { ready: true, attempts: i };
|
|
15212
|
+
} else {
|
|
15213
|
+
const cls = classifyFoundryError({ status: r.status, message: r.message ?? "" });
|
|
15214
|
+
const forcedRetryable = r.status !== void 0 && (opts.retryableStatuses?.includes(r.status) ?? false);
|
|
15215
|
+
if (!forcedRetryable && !cls.retryable) {
|
|
15216
|
+
throw new LocalCliError({
|
|
15217
|
+
code: "FOUNDRY_NOT_READY_FATAL",
|
|
15218
|
+
message: `Foundry data plane returned a non-retryable error: ${cls.message}`,
|
|
15219
|
+
hint: "This is not a transient data-plane delay \u2014 check `az login`/permissions and the endpoint.",
|
|
15220
|
+
retryable: false
|
|
15221
|
+
});
|
|
15222
|
+
}
|
|
15223
|
+
streak = 0;
|
|
15224
|
+
opts.onProgress?.(`data-plane not ready (${forcedRetryable ? `status ${(r.status ?? 0).toString()}` : cls.category}), waiting\u2026`);
|
|
15225
|
+
}
|
|
15226
|
+
if (i < attempts) await sleep2(intervalMs);
|
|
15227
|
+
}
|
|
15228
|
+
return { ready: false, attempts };
|
|
15229
|
+
}
|
|
15230
|
+
|
|
15231
|
+
// src/lib/foundry-ready-probe.ts
|
|
15232
|
+
init_http();
|
|
15233
|
+
var FOUNDRY_SCOPE3 = "https://ai.azure.com/.default";
|
|
15234
|
+
function makeAgentsProbe(credential2, endpoint) {
|
|
15235
|
+
return async () => {
|
|
15236
|
+
try {
|
|
15237
|
+
const r = await authedJsonResult({
|
|
15238
|
+
credential: credential2,
|
|
15239
|
+
scope: FOUNDRY_SCOPE3,
|
|
15240
|
+
method: "GET",
|
|
15241
|
+
url: `${endpoint}/agents?api-version=v1`,
|
|
15242
|
+
okStatuses: [404, 500, 502, 503]
|
|
15243
|
+
// don't throw — return the status so the loop classifies it
|
|
15244
|
+
});
|
|
15245
|
+
if (r.status === 200) return { ok: true, status: 200, message: "" };
|
|
15246
|
+
const body = r.data?.error?.message ?? "";
|
|
15247
|
+
return { ok: false, status: r.status, message: `${r.status.toString()} ${body}`.trim() };
|
|
15248
|
+
} catch (e) {
|
|
15249
|
+
return { ok: false, message: e instanceof Error ? e.message : String(e) };
|
|
15250
|
+
}
|
|
15251
|
+
};
|
|
15252
|
+
}
|
|
15253
|
+
function makeAgentProbe(credential2, endpoint, agentName) {
|
|
15254
|
+
return async () => {
|
|
15255
|
+
try {
|
|
15256
|
+
const r = await authedJsonResult({
|
|
15257
|
+
credential: credential2,
|
|
15258
|
+
scope: FOUNDRY_SCOPE3,
|
|
15259
|
+
method: "GET",
|
|
15260
|
+
url: `${endpoint}/agents/${agentName}?api-version=v1`,
|
|
15261
|
+
okStatuses: [404, 500, 502, 503]
|
|
15262
|
+
// don't throw — return the status so the loop classifies it
|
|
15263
|
+
});
|
|
15264
|
+
if (r.status === 200) return { ok: true, status: 200, message: "" };
|
|
15265
|
+
const body = r.data?.error?.message ?? "";
|
|
15266
|
+
return { ok: false, status: r.status, message: `${r.status.toString()} ${body}`.trim() };
|
|
15267
|
+
} catch (e) {
|
|
15268
|
+
return { ok: false, message: e instanceof Error ? e.message : String(e) };
|
|
15269
|
+
}
|
|
15270
|
+
};
|
|
15271
|
+
}
|
|
15272
|
+
|
|
15273
|
+
// src/lib/agent-ready.ts
|
|
15274
|
+
init_errors();
|
|
15275
|
+
async function awaitAgentQueryable(args) {
|
|
15276
|
+
const attemptsMax = args.attempts ?? 40;
|
|
15277
|
+
const intervalMs = args.intervalMs ?? 3e3;
|
|
15278
|
+
const probe = args.probe ?? makeAgentProbe(args.credential, args.projectEndpoint, args.agentName);
|
|
15279
|
+
const { ready, attempts } = await awaitDataPlaneReady({
|
|
15280
|
+
probe,
|
|
15281
|
+
consecutive: args.consecutive ?? 2,
|
|
15282
|
+
attempts: attemptsMax,
|
|
15283
|
+
intervalMs,
|
|
15284
|
+
sleep: args.sleep,
|
|
15285
|
+
onProgress: args.onProgress,
|
|
15286
|
+
retryableStatuses: [404]
|
|
15287
|
+
});
|
|
15288
|
+
if (!ready) {
|
|
15289
|
+
const approxSec = Math.round(attemptsMax * intervalMs / 1e3);
|
|
15290
|
+
throw new LocalCliError({
|
|
15291
|
+
code: "AGENT_NOT_QUERYABLE",
|
|
15292
|
+
message: `Agent '${args.agentName}' did not become queryable within ${attempts.toString()} probes (~${approxSec.toString()}s) after deploy.`,
|
|
15293
|
+
hint: "Foundry agent registration propagates asynchronously after deploy; a retry usually clears it. Re-run shortly (e.g. 'm8t bootstrap launch' or 'm8t a2a enable <name>').",
|
|
15294
|
+
category: "data_plane_not_ready",
|
|
15295
|
+
retryable: false
|
|
15296
|
+
});
|
|
15297
|
+
}
|
|
15298
|
+
return { attempts };
|
|
15299
|
+
}
|
|
15300
|
+
|
|
15199
15301
|
// src/lib/persona-a2a.ts
|
|
15200
15302
|
init_errors();
|
|
15201
15303
|
init_esm();
|
|
@@ -15251,6 +15353,13 @@ async function enableA2a(args) {
|
|
|
15251
15353
|
const connectionName = `a2a-${args.agentName}`;
|
|
15252
15354
|
progress("Reading persona a2a-card\u2026");
|
|
15253
15355
|
const { serialized: a2aCardJson } = readPersonaA2aCard(args.personaPath);
|
|
15356
|
+
progress("Waiting for the agent to become queryable\u2026");
|
|
15357
|
+
await awaitAgentQueryable({
|
|
15358
|
+
credential: args.credential,
|
|
15359
|
+
projectEndpoint: args.projectEndpoint,
|
|
15360
|
+
agentName: args.agentName,
|
|
15361
|
+
onProgress: args.onProgress
|
|
15362
|
+
});
|
|
15254
15363
|
progress("Reading current agent definition\u2026");
|
|
15255
15364
|
const current = await getAgentVersion({ credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName });
|
|
15256
15365
|
if (current.definition.kind === "hosted") {
|
|
@@ -22147,62 +22256,6 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
22147
22256
|
// src/commands/foundry/await-ready.ts
|
|
22148
22257
|
import { Command as Command44, Option as Option42 } from "clipanion";
|
|
22149
22258
|
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
22150
|
-
|
|
22151
|
-
// src/lib/data-plane-ready.ts
|
|
22152
|
-
init_errors();
|
|
22153
|
-
async function awaitDataPlaneReady(opts) {
|
|
22154
|
-
const consecutive = opts.consecutive ?? 3;
|
|
22155
|
-
const attempts = opts.attempts ?? 60;
|
|
22156
|
-
const intervalMs = opts.intervalMs ?? 5e3;
|
|
22157
|
-
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
22158
|
-
let streak = 0;
|
|
22159
|
-
for (let i = 1; i <= attempts; i++) {
|
|
22160
|
-
const r = await opts.probe();
|
|
22161
|
-
if (r.ok) {
|
|
22162
|
-
streak++;
|
|
22163
|
-
opts.onProgress?.(`data-plane ok (${streak.toString()}/${consecutive.toString()})`);
|
|
22164
|
-
if (streak >= consecutive) return { ready: true, attempts: i };
|
|
22165
|
-
} else {
|
|
22166
|
-
const cls = classifyFoundryError({ status: r.status, message: r.message ?? "" });
|
|
22167
|
-
if (!cls.retryable) {
|
|
22168
|
-
throw new LocalCliError({
|
|
22169
|
-
code: "FOUNDRY_NOT_READY_FATAL",
|
|
22170
|
-
message: `Foundry data plane returned a non-retryable error: ${cls.message}`,
|
|
22171
|
-
hint: "This is not a transient data-plane delay \u2014 check `az login`/permissions and the endpoint."
|
|
22172
|
-
});
|
|
22173
|
-
}
|
|
22174
|
-
streak = 0;
|
|
22175
|
-
opts.onProgress?.(`data-plane not ready (${cls.category}), waiting\u2026`);
|
|
22176
|
-
}
|
|
22177
|
-
if (i < attempts) await sleep2(intervalMs);
|
|
22178
|
-
}
|
|
22179
|
-
return { ready: false, attempts };
|
|
22180
|
-
}
|
|
22181
|
-
|
|
22182
|
-
// src/lib/foundry-ready-probe.ts
|
|
22183
|
-
init_http();
|
|
22184
|
-
var FOUNDRY_SCOPE3 = "https://ai.azure.com/.default";
|
|
22185
|
-
function makeAgentsProbe(credential2, endpoint) {
|
|
22186
|
-
return async () => {
|
|
22187
|
-
try {
|
|
22188
|
-
const r = await authedJsonResult({
|
|
22189
|
-
credential: credential2,
|
|
22190
|
-
scope: FOUNDRY_SCOPE3,
|
|
22191
|
-
method: "GET",
|
|
22192
|
-
url: `${endpoint}/agents?api-version=v1`,
|
|
22193
|
-
okStatuses: [404, 500, 502, 503]
|
|
22194
|
-
// don't throw — return the status so the loop classifies it
|
|
22195
|
-
});
|
|
22196
|
-
if (r.status === 200) return { ok: true, status: 200, message: "" };
|
|
22197
|
-
const body = r.data?.error?.message ?? "";
|
|
22198
|
-
return { ok: false, status: r.status, message: `${r.status.toString()} ${body}`.trim() };
|
|
22199
|
-
} catch (e) {
|
|
22200
|
-
return { ok: false, message: e instanceof Error ? e.message : String(e) };
|
|
22201
|
-
}
|
|
22202
|
-
};
|
|
22203
|
-
}
|
|
22204
|
-
|
|
22205
|
-
// src/commands/foundry/await-ready.ts
|
|
22206
22259
|
init_errors();
|
|
22207
22260
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
22208
22261
|
static paths = [["foundry", "await-ready"]];
|
|
@@ -23106,9 +23159,9 @@ function companyProfileBodyEquals(a, b) {
|
|
|
23106
23159
|
const strip = (s) => s.replace(/^(created|updated):.*$/gm, "").trim();
|
|
23107
23160
|
return strip(a) === strip(b);
|
|
23108
23161
|
}
|
|
23109
|
-
function upsertMemoryIndex(existing, line2) {
|
|
23162
|
+
function upsertMemoryIndex(existing, line2, targetPath2 = COMPANY_PROFILE_PATH) {
|
|
23110
23163
|
const lines = existing.split("\n");
|
|
23111
|
-
const existingIdx = lines.findIndex((l) => l.includes(`\`${
|
|
23164
|
+
const existingIdx = lines.findIndex((l) => l.includes(`\`${targetPath2}\``));
|
|
23112
23165
|
if (existingIdx >= 0) {
|
|
23113
23166
|
lines[existingIdx] = line2;
|
|
23114
23167
|
return lines.join("\n");
|
|
@@ -23120,6 +23173,88 @@ function upsertMemoryIndex(existing, line2) {
|
|
|
23120
23173
|
}
|
|
23121
23174
|
return existing.replace(/\s*$/, "\n\n") + line2 + "\n";
|
|
23122
23175
|
}
|
|
23176
|
+
var FOUNDER_RECORD_PATH = "memory/founder.md";
|
|
23177
|
+
var NOT_CAPTURED = "_not captured yet \u2014 add it anytime (run `m8t bootstrap seed-profile`, or just tell me)_";
|
|
23178
|
+
function renderFounderRecord(block, inputs, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
23179
|
+
const pick = (...vals) => vals.map((v) => v?.trim()).find(Boolean) ?? "";
|
|
23180
|
+
const founderName = pick(block.founder_name, inputs.azIdentity?.name);
|
|
23181
|
+
const founderEmail = pick(block.founder_email, inputs.azIdentity?.email);
|
|
23182
|
+
const advisorName = (block.advisor_name ?? "").trim();
|
|
23183
|
+
const advisorEmail = (block.advisor_email ?? "").trim();
|
|
23184
|
+
const subscription = (inputs.subscriptionId ?? "").trim();
|
|
23185
|
+
const teamSize = (block.team_size ?? "").trim();
|
|
23186
|
+
const advisorRendered = advisorName || advisorEmail ? [advisorName, advisorEmail ? `<${advisorEmail}>` : ""].filter(Boolean).join(" ") : NOT_CAPTURED;
|
|
23187
|
+
const founderMd = [
|
|
23188
|
+
`---`,
|
|
23189
|
+
`type: memory`,
|
|
23190
|
+
`title: "Founder & install context"`,
|
|
23191
|
+
`created: ${now}`,
|
|
23192
|
+
`updated: ${now}`,
|
|
23193
|
+
`tags: [founder, install-context, onboarding]`,
|
|
23194
|
+
`origin: operator`,
|
|
23195
|
+
`---`,
|
|
23196
|
+
``,
|
|
23197
|
+
`# Founder & install context`,
|
|
23198
|
+
``,
|
|
23199
|
+
`_Seeded at onboarding from your Azure identity + the questionnaire. Authoritative \u2014 read it; don't rewrite it (origin: operator)._`,
|
|
23200
|
+
``,
|
|
23201
|
+
`- **Founder:** ${founderName || NOT_CAPTURED}`,
|
|
23202
|
+
`- **Founder email (company_email):** ${founderEmail || NOT_CAPTURED}`,
|
|
23203
|
+
`- **Microsoft Startup Advisor (SA):** ${advisorRendered}`,
|
|
23204
|
+
`- **Azure subscription:** ${subscription || NOT_CAPTURED}`,
|
|
23205
|
+
`- **Team size:** ${teamSize || "\u2014"}`,
|
|
23206
|
+
``
|
|
23207
|
+
].join("\n");
|
|
23208
|
+
const idxAdvisor = advisorEmail || advisorName || "\u2014";
|
|
23209
|
+
const idxSub = subscription ? `${subscription.slice(0, 8)}\u2026` : "\u2014";
|
|
23210
|
+
const memoryIndexLine = `- \`${FOUNDER_RECORD_PATH}\` \u2014 **Founder & install context**: ${founderName || "founder"} \xB7 advisor ${idxAdvisor} \xB7 sub ${idxSub}. (seeded from onboarding)`;
|
|
23211
|
+
return { founderMd, memoryIndexLine };
|
|
23212
|
+
}
|
|
23213
|
+
function founderRecordBodyEquals(a, b) {
|
|
23214
|
+
const strip = (s) => s.replace(/^(created|updated):.*$/gm, "").trim();
|
|
23215
|
+
return strip(a) === strip(b);
|
|
23216
|
+
}
|
|
23217
|
+
|
|
23218
|
+
// src/lib/founder-identity.ts
|
|
23219
|
+
function deriveEmailCandidate(raw) {
|
|
23220
|
+
const mail = (raw.mail ?? "").trim();
|
|
23221
|
+
if (mail) return mail;
|
|
23222
|
+
const upn = (raw.upn ?? "").trim();
|
|
23223
|
+
if (!upn) return "";
|
|
23224
|
+
const ext = upn.indexOf("#EXT#");
|
|
23225
|
+
if (ext > 0) {
|
|
23226
|
+
const local = upn.slice(0, ext);
|
|
23227
|
+
const at = local.lastIndexOf("_");
|
|
23228
|
+
return at > 0 ? `${local.slice(0, at)}@${local.slice(at + 1)}` : "";
|
|
23229
|
+
}
|
|
23230
|
+
return upn.includes("@") ? upn : "";
|
|
23231
|
+
}
|
|
23232
|
+
async function getSignedInUserIdentity(runAzImpl = runAz) {
|
|
23233
|
+
try {
|
|
23234
|
+
const raw = await runAzImpl([
|
|
23235
|
+
"ad",
|
|
23236
|
+
"signed-in-user",
|
|
23237
|
+
"show",
|
|
23238
|
+
"--query",
|
|
23239
|
+
"{displayName:displayName, mail:mail, upn:userPrincipalName}",
|
|
23240
|
+
"-o",
|
|
23241
|
+
"json"
|
|
23242
|
+
]);
|
|
23243
|
+
const p = JSON.parse(raw);
|
|
23244
|
+
return { name: (p.displayName ?? "").trim(), email: deriveEmailCandidate({ mail: p.mail, upn: p.upn }) };
|
|
23245
|
+
} catch {
|
|
23246
|
+
return { name: "", email: "" };
|
|
23247
|
+
}
|
|
23248
|
+
}
|
|
23249
|
+
function composeFounderIdentityNote(id) {
|
|
23250
|
+
if (id.name && id.email) {
|
|
23251
|
+
return `You're speaking with **${id.name}**, signed in to Azure (their email looks like **${id.email}**). Open by confirming both, in your own voice \u2014 e.g. "I see you're signed in as ${id.name}, and I'll use ${id.email} for your advisor handoffs and cost reports \u2014 is that right, or should I change either?" Record exactly what they approve or correct.`;
|
|
23252
|
+
}
|
|
23253
|
+
if (id.name) {
|
|
23254
|
+
return `You're speaking with **${id.name}** (signed in to Azure). Confirm their name, then ask for the best contact email to reach them (that's where advisor handoffs and cost reports go).`;
|
|
23255
|
+
}
|
|
23256
|
+
return `Ask the founder their name and the best contact email to reach them (that's where advisor handoffs and cost reports go).`;
|
|
23257
|
+
}
|
|
23123
23258
|
|
|
23124
23259
|
// src/lib/company-profile-seed.ts
|
|
23125
23260
|
function readGithubAppCreds(credsPath) {
|
|
@@ -23134,18 +23269,21 @@ async function resolveSeedContext(opts) {
|
|
|
23134
23269
|
const appCreds = opts.appCredsOverride ?? readGithubAppCreds(opts.credsPath);
|
|
23135
23270
|
if (!appCreds) return null;
|
|
23136
23271
|
let endpoint = opts.endpointOverride;
|
|
23272
|
+
let subscriptionId = opts.subscriptionId;
|
|
23137
23273
|
if (!endpoint) {
|
|
23138
23274
|
const state = await readBootstrapState();
|
|
23139
23275
|
if (!state) {
|
|
23140
23276
|
throw new LocalCliError({ code: "BOOTSTRAP_NO_STATE", message: "No bootstrap state found.", hint: "Run 'm8t bootstrap launch' first, or pass --endpoint." });
|
|
23141
23277
|
}
|
|
23278
|
+
subscriptionId ??= state.subscriptionId;
|
|
23142
23279
|
const doc = await readStatusBlob({ saName: state.statusSaName, resourceGroup: state.resourceGroup, subscriptionId: state.subscriptionId });
|
|
23143
23280
|
endpoint = doc.result?.foundryEndpoint;
|
|
23144
23281
|
if (!endpoint) {
|
|
23145
23282
|
throw new LocalCliError({ code: "SEED_NO_ENDPOINT", message: "Could not resolve the Foundry endpoint from the install status.", hint: "Pass --endpoint <foundryEndpoint>." });
|
|
23146
23283
|
}
|
|
23147
23284
|
}
|
|
23148
|
-
|
|
23285
|
+
const brainRepos = opts.brainOverride ? [opts.brainOverride] : [`${appCreds.org}/stacey-brain`, `${appCreds.org}/azzy-brain`];
|
|
23286
|
+
return { endpoint, brainRepos, appCreds, subscriptionId };
|
|
23149
23287
|
}
|
|
23150
23288
|
async function applyProfileToBrain(args) {
|
|
23151
23289
|
const token = await mintInstallationTokenFromPem({
|
|
@@ -23154,20 +23292,31 @@ async function applyProfileToBrain(args) {
|
|
|
23154
23292
|
installationId: args.appCreds.installationId,
|
|
23155
23293
|
fetchImpl: args.fetchImpl
|
|
23156
23294
|
});
|
|
23157
|
-
const { profileMd, memoryIndexLine } = renderCompanyProfile(args.block, args.now);
|
|
23158
|
-
const
|
|
23159
|
-
|
|
23160
|
-
|
|
23161
|
-
|
|
23295
|
+
const { profileMd, memoryIndexLine: companyLine } = renderCompanyProfile(args.block, args.now);
|
|
23296
|
+
const { founderMd, memoryIndexLine: founderLine } = renderFounderRecord(
|
|
23297
|
+
args.block,
|
|
23298
|
+
{ subscriptionId: args.subscriptionId, azIdentity: args.azIdentity },
|
|
23299
|
+
args.now
|
|
23300
|
+
);
|
|
23301
|
+
const read = (p) => readRepoFileViaApp({ token, repo: args.brainRepo, path: p, fetchImpl: args.fetchImpl });
|
|
23302
|
+
const existingProfile = await read(COMPANY_PROFILE_PATH);
|
|
23303
|
+
const existingFounder = await read(FOUNDER_RECORD_PATH);
|
|
23304
|
+
const existingIndex = await read("memory/MEMORY.md") ?? DEFAULT_MEMORY_INDEX_HEADER;
|
|
23305
|
+
let nextIndex = upsertMemoryIndex(existingIndex, companyLine, COMPANY_PROFILE_PATH);
|
|
23306
|
+
nextIndex = upsertMemoryIndex(nextIndex, founderLine, FOUNDER_RECORD_PATH);
|
|
23307
|
+
const profileSame = existingProfile != null && companyProfileBodyEquals(existingProfile, profileMd);
|
|
23308
|
+
const founderSame = existingFounder != null && founderRecordBodyEquals(existingFounder, founderMd);
|
|
23309
|
+
if (profileSame && founderSame && nextIndex === existingIndex) {
|
|
23162
23310
|
return;
|
|
23163
23311
|
}
|
|
23164
23312
|
await commitFilesViaApp({
|
|
23165
23313
|
token,
|
|
23166
23314
|
repo: args.brainRepo,
|
|
23167
23315
|
branch: args.branch,
|
|
23168
|
-
message: "seed(brain): company profile from onboarding
|
|
23316
|
+
message: "seed(brain): founder + company profile from onboarding",
|
|
23169
23317
|
files: [
|
|
23170
23318
|
{ path: COMPANY_PROFILE_PATH, content: profileMd },
|
|
23319
|
+
{ path: FOUNDER_RECORD_PATH, content: founderMd },
|
|
23171
23320
|
{ path: "memory/MEMORY.md", content: nextIndex }
|
|
23172
23321
|
],
|
|
23173
23322
|
fetchImpl: args.fetchImpl
|
|
@@ -23187,20 +23336,31 @@ function spawnDetachedSeedWatch() {
|
|
|
23187
23336
|
}
|
|
23188
23337
|
}
|
|
23189
23338
|
async function reactiveSeedOnFinish(args) {
|
|
23190
|
-
const ctx = await resolveSeedContext({ endpointOverride: args.endpoint, appCredsOverride: args.appCredsOverride });
|
|
23339
|
+
const ctx = await resolveSeedContext({ endpointOverride: args.endpoint, appCredsOverride: args.appCredsOverride, subscriptionId: args.subscriptionId });
|
|
23191
23340
|
if (!ctx) return;
|
|
23192
23341
|
const token = await (args.getFoundryTokenImpl ?? getFoundryToken)();
|
|
23193
23342
|
const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token, fetchImpl: args.fetchImpl });
|
|
23194
23343
|
if (block) {
|
|
23195
|
-
|
|
23196
|
-
|
|
23344
|
+
const azIdentity = args.azIdentity ?? await (args.getSignedInUserIdentityImpl ?? getSignedInUserIdentity)();
|
|
23345
|
+
for (const brainRepo of ctx.brainRepos) {
|
|
23346
|
+
await applyProfileToBrain({
|
|
23347
|
+
block,
|
|
23348
|
+
brainRepo,
|
|
23349
|
+
branch: "main",
|
|
23350
|
+
appCreds: ctx.appCreds,
|
|
23351
|
+
subscriptionId: ctx.subscriptionId,
|
|
23352
|
+
azIdentity,
|
|
23353
|
+
fetchImpl: args.fetchImpl
|
|
23354
|
+
});
|
|
23355
|
+
}
|
|
23356
|
+
args.stdout(`${colors.success("\u2713")} Your advisors now know your company + how to reach you (seeded ${ctx.brainRepos.join(", ")}).
|
|
23197
23357
|
`);
|
|
23198
23358
|
return;
|
|
23199
23359
|
}
|
|
23200
23360
|
if (!hadIntake) return;
|
|
23201
23361
|
(args.spawnWatch ?? spawnDetachedSeedWatch)();
|
|
23202
23362
|
args.stdout(
|
|
23203
|
-
`${colors.dim("\u2139
|
|
23363
|
+
`${colors.dim("\u2139 Your advisors will pick up your company profile when you finish the questionnaire (watching in the background).")}
|
|
23204
23364
|
${colors.hint("or run:")} m8t bootstrap seed-profile
|
|
23205
23365
|
`
|
|
23206
23366
|
);
|
|
@@ -23317,6 +23477,7 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
|
|
|
23317
23477
|
try {
|
|
23318
23478
|
await reactiveSeedOnFinish({
|
|
23319
23479
|
endpoint: doc.result?.foundryEndpoint,
|
|
23480
|
+
subscriptionId,
|
|
23320
23481
|
stdout: (s) => this.context.stdout.write(s)
|
|
23321
23482
|
});
|
|
23322
23483
|
} catch (e) {
|
|
@@ -23563,7 +23724,8 @@ async function deploySimpleStacey(args) {
|
|
|
23563
23724
|
endpoint: args.endpoint,
|
|
23564
23725
|
repoRoot: args.repoRoot,
|
|
23565
23726
|
persona: SIMPLE_STACEY_PERSONA,
|
|
23566
|
-
agentName: SIMPLE_STACEY_AGENT
|
|
23727
|
+
agentName: SIMPLE_STACEY_AGENT,
|
|
23728
|
+
fieldOverrides: args.fieldOverrides
|
|
23567
23729
|
});
|
|
23568
23730
|
} catch (e) {
|
|
23569
23731
|
if (e && typeof e === "object" && "code" in e) {
|
|
@@ -23681,7 +23843,13 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
23681
23843
|
const oid = await getSignedInUserOid();
|
|
23682
23844
|
await ensureFounderFoundryRole({ credential: credential2, subscriptionId: state.subscriptionId, principalId: oid, accountScope });
|
|
23683
23845
|
out("deploying Simple Stacey (stacey-intake)\u2026");
|
|
23684
|
-
const
|
|
23846
|
+
const identity = await getSignedInUserIdentity();
|
|
23847
|
+
const version = await deploySimpleStacey({
|
|
23848
|
+
credential: credential2,
|
|
23849
|
+
endpoint,
|
|
23850
|
+
repoRoot,
|
|
23851
|
+
fieldOverrides: { founder_identity_note: composeFounderIdentityNote(identity) }
|
|
23852
|
+
});
|
|
23685
23853
|
const envPath = writeWebEnvLocal({
|
|
23686
23854
|
repoRoot,
|
|
23687
23855
|
tenantId: account.tenantId,
|
|
@@ -23734,8 +23902,8 @@ import { Command as Command51, Option as Option49 } from "clipanion";
|
|
|
23734
23902
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
23735
23903
|
static paths = [["bootstrap", "seed-profile"]];
|
|
23736
23904
|
static usage = Command51.Usage({
|
|
23737
|
-
description: "Seed
|
|
23738
|
-
details: "Reads the latest stacey-intake conversation, renders memory/company-profile.md (+
|
|
23905
|
+
description: "Seed your advisors' brains with the founder + company profile from the onboarding questionnaire.",
|
|
23906
|
+
details: "Reads the latest stacey-intake conversation, renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines), and commits them to both <org>/stacey-brain and <org>/azzy-brain via the GitHub App. Idempotent. --watch polls until the founder completes the questionnaire.",
|
|
23739
23907
|
examples: [
|
|
23740
23908
|
["Seed now (idempotent)", "$0 bootstrap seed-profile"],
|
|
23741
23909
|
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
@@ -23767,8 +23935,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
23767
23935
|
const token = await getFoundryToken();
|
|
23768
23936
|
const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token });
|
|
23769
23937
|
if (block) {
|
|
23770
|
-
|
|
23771
|
-
|
|
23938
|
+
const azIdentity = await getSignedInUserIdentity();
|
|
23939
|
+
for (const brainRepo of ctx.brainRepos) {
|
|
23940
|
+
await applyProfileToBrain({ block, brainRepo, branch: "main", appCreds: ctx.appCreds, subscriptionId: ctx.subscriptionId, azIdentity });
|
|
23941
|
+
}
|
|
23942
|
+
this.context.stdout.write(`${colors.success("\u2713")} seeded your advisors' brains (${ctx.brainRepos.join(", ")}) from your questionnaire.
|
|
23772
23943
|
`);
|
|
23773
23944
|
return 0;
|
|
23774
23945
|
}
|