@dosu/cli 0.11.0-alpha.3 → 0.11.0-alpha.4

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.
Files changed (2) hide show
  1. package/bin/dosu.js +128 -21
  2. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -4341,7 +4341,7 @@ function getSupabaseAnonKey() {
4341
4341
  function getVersionString() {
4342
4342
  return `v${VERSION}`;
4343
4343
  }
4344
- var VERSION = "0.11.0-alpha.3";
4344
+ var VERSION = "0.11.0-alpha.4";
4345
4345
 
4346
4346
  // src/debug/logger.ts
4347
4347
  import {
@@ -7148,6 +7148,7 @@ var init_open = __esm(() => {
7148
7148
  // src/setup/github-step.ts
7149
7149
  var exports_github_step = {};
7150
7150
  __export(exports_github_step, {
7151
+ verifyDataSourcesPersist: () => verifyDataSourcesPersist,
7151
7152
  stepConnectGitHubRepo: () => stepConnectGitHubRepo,
7152
7153
  detectGitRepo: () => detectGitRepo
7153
7154
  });
@@ -7175,24 +7176,32 @@ function detectGitRepo(cwd = process.cwd()) {
7175
7176
  }
7176
7177
  async function fetchListForOrg(trpc, orgID) {
7177
7178
  try {
7178
- return await trpc.githubRepository.listForOrg.query({
7179
+ const repos = await trpc.githubRepository.listForOrg.query({
7179
7180
  org_id: orgID
7180
7181
  });
7182
+ return sortReposByRecency(repos);
7181
7183
  } catch (err) {
7182
7184
  const msg = err instanceof Error ? err.message : String(err);
7183
7185
  logger.warn("setup", `listForOrg failed: ${msg}`);
7184
7186
  return [];
7185
7187
  }
7186
7188
  }
7189
+ function sortReposByRecency(repos) {
7190
+ return [...repos].sort((a, b3) => {
7191
+ const ta = Date.parse(a.created_at ?? "") || 0;
7192
+ const tb = Date.parse(b3.created_at ?? "") || 0;
7193
+ return tb - ta;
7194
+ });
7195
+ }
7187
7196
  function buildPromptOptions(repos) {
7188
7197
  return [
7189
- ...repos.map((r) => ({ kind: "repo", label: r.slug, value: r.slug })),
7190
7198
  {
7191
7199
  kind: "action",
7192
7200
  label: "Add repositories...",
7193
7201
  value: ADD_REPOSITORIES_VALUE,
7194
7202
  hint: "Open GitHub and refresh this list"
7195
- }
7203
+ },
7204
+ ...repos.map((r) => ({ kind: "repo", label: r.slug, value: r.slug }))
7196
7205
  ];
7197
7206
  }
7198
7207
  function hasNewVisibleRepository(previousRepos, nextRepos) {
@@ -7297,14 +7306,59 @@ async function createDeploymentForRepo(trpc, orgID, spaceID, repo) {
7297
7306
  deployment_id: d3.deployment_id,
7298
7307
  data_source_id: dataSource.data_source_id
7299
7308
  })));
7300
- return { deployment_id: deployment.deployment_id };
7309
+ return {
7310
+ deployment_id: deployment.deployment_id,
7311
+ data_source_id: dataSource.data_source_id
7312
+ };
7301
7313
  } catch (err) {
7302
7314
  const msg = err instanceof Error ? err.message : String(err);
7303
7315
  logger.error("setup", `Failed to wire up ${repo.slug}: ${msg}`);
7304
7316
  return null;
7305
7317
  }
7306
7318
  }
7307
- async function stepConnectGitHubRepo(cfg, detected = detectGitRepo()) {
7319
+ async function verifyDataSourcesPersist(trpc, orgID, expectedDataSourceIds, opts = {}) {
7320
+ const expected = new Set(expectedDataSourceIds);
7321
+ if (expected.size === 0) {
7322
+ return { alive: new Set, dropped: new Set };
7323
+ }
7324
+ const timeoutMs = opts.timeoutMs ?? DATA_SOURCE_VERIFY_POLL_TIMEOUT_MS;
7325
+ const intervalMs = opts.intervalMs ?? DATA_SOURCE_VERIFY_POLL_INTERVAL_MS;
7326
+ const startedAt = Date.now();
7327
+ let alive = new Set;
7328
+ let dropped = new Set;
7329
+ let firstIteration = true;
7330
+ while (firstIteration || Date.now() - startedAt < timeoutMs) {
7331
+ firstIteration = false;
7332
+ let listed = [];
7333
+ try {
7334
+ listed = await trpc.dataSource.list.query({
7335
+ org_id: orgID,
7336
+ excluded_provider_slugs: []
7337
+ });
7338
+ } catch (err) {
7339
+ const msg = err instanceof Error ? err.message : String(err);
7340
+ logger.warn("setup", `dataSource.list during verify failed: ${msg}`);
7341
+ }
7342
+ const presentNow = new Set(listed.map((d3) => d3.data_source_id).filter((id) => Boolean(id)));
7343
+ alive = new Set([...expected].filter((id) => presentNow.has(id)));
7344
+ dropped = new Set([...expected].filter((id) => !presentNow.has(id)));
7345
+ if (dropped.size > 0)
7346
+ return { alive, dropped };
7347
+ if (timeoutMs === 0)
7348
+ break;
7349
+ await sleep2(intervalMs);
7350
+ }
7351
+ return { alive, dropped };
7352
+ }
7353
+ async function deleteOrphanDeployment(trpc, deploymentID, slug) {
7354
+ try {
7355
+ await trpc.workspaces.delete.mutate(deploymentID);
7356
+ } catch (err) {
7357
+ const msg = err instanceof Error ? err.message : String(err);
7358
+ logger.warn("setup", `Failed to revert orphan deployment for ${slug}: ${msg}`);
7359
+ }
7360
+ }
7361
+ async function stepConnectGitHubRepo(cfg, detected = detectGitRepo(), opts = {}) {
7308
7362
  logger.info("setup", "Step: connect GitHub repo(s)");
7309
7363
  if (!cfg.org_id || !cfg.space_id) {
7310
7364
  M2.warn("Cannot connect GitHub: your Dosu workspace is missing org/space context. Re-run `dosu setup` from a fresh state.");
@@ -7327,9 +7381,10 @@ async function stepConnectGitHubRepo(cfg, detected = detectGitRepo()) {
7327
7381
  ${lines}`);
7328
7382
  }
7329
7383
  const selected = await promptGitHubRepositories({
7330
- message: "Select repositories to connect",
7384
+ message: `Select repositories to connect ${dim(`(${undeployed.length} available)`)}`,
7331
7385
  options: buildPromptOptions(undeployed),
7332
- initialValues: []
7386
+ initialValues: [],
7387
+ maxItems: REPO_MULTISELECT_MAX_ITEMS
7333
7388
  });
7334
7389
  if (pD(selected)) {
7335
7390
  logger.info("setup", "Repository selection cancelled");
@@ -7360,29 +7415,55 @@ ${lines}`);
7360
7415
  continue;
7361
7416
  const result = await createDeploymentForRepo(trpc, orgID, spaceID, repo);
7362
7417
  if (result) {
7363
- created.push({ deployment_id: result.deployment_id, slug });
7418
+ created.push({
7419
+ deployment_id: result.deployment_id,
7420
+ data_source_id: result.data_source_id,
7421
+ slug
7422
+ });
7423
+ }
7424
+ }
7425
+ const expectedDsIds = created.map((c) => c.data_source_id).filter((id) => Boolean(id));
7426
+ const survivors = await verifyDataSourcesPersist(trpc, orgID, expectedDsIds, opts.verify);
7427
+ const reverted = [];
7428
+ if (survivors.dropped.size > 0) {
7429
+ const droppedEntries = created.filter((c) => c.data_source_id && survivors.dropped.has(c.data_source_id));
7430
+ for (const entry of droppedEntries) {
7431
+ await deleteOrphanDeployment(trpc, entry.deployment_id, entry.slug);
7432
+ reverted.push({ slug: entry.slug, deployment_id: entry.deployment_id });
7364
7433
  }
7365
7434
  }
7366
- if (created.length === 0) {
7435
+ const survived = created.filter((c) => !c.data_source_id || survivors.alive.has(c.data_source_id));
7436
+ if (survived.length === 0) {
7367
7437
  s.stop("Failed");
7368
- M2.error("Could not connect any repos. Check `dosu logs --tail 50` for details.");
7438
+ if (reverted.length > 0) {
7439
+ M2.error(`Couldn't sync any repos — Dosu doesn't have GitHub access to: ${reverted.map((r) => r.slug).join(", ")}.`);
7440
+ } else {
7441
+ M2.error("Could not connect any repos. Check `dosu logs --tail 50` for details.");
7442
+ }
7369
7443
  return { advance: false, has_connected_repo: deployed.length > 0 };
7370
7444
  }
7371
- s.stop(`Connected ${created.length} repo${created.length === 1 ? "" : "s"}`);
7372
- for (const { slug, deployment_id } of created) {
7445
+ if (reverted.length > 0) {
7446
+ s.stop(`Connected ${survived.length} repo${survived.length === 1 ? "" : "s"} · ${reverted.length} skipped`);
7447
+ M2.warn(`Dosu couldn't sync ${reverted.length} repo${reverted.length === 1 ? "" : "s"} (GitHub App has no access): ${reverted.map((r) => r.slug).join(", ")}`);
7448
+ } else {
7449
+ s.stop(`Connected ${survived.length} repo${survived.length === 1 ? "" : "s"}`);
7450
+ }
7451
+ for (const { slug, deployment_id } of survived) {
7373
7452
  M2.success(`${slug}
7374
7453
  ${dim(`deployment ${deployment_id}`)}`);
7375
7454
  }
7376
- const primary = (detected && created.find((c) => c.slug === detected.slug)) ?? created[0];
7455
+ const primary = (detected && survived.find((c) => c.slug === detected.slug)) ?? survived[0];
7456
+ const survivingDataSourceIds = survived.map((c) => c.data_source_id).filter((id) => Boolean(id));
7377
7457
  return {
7378
7458
  advance: true,
7379
7459
  has_connected_repo: true,
7380
7460
  deployment_id: primary.deployment_id,
7381
- space_id: cfg.space_id
7461
+ space_id: cfg.space_id,
7462
+ created_data_source_ids: survivingDataSourceIds
7382
7463
  };
7383
7464
  }
7384
7465
  }
7385
- var INSTALLATION_TIMEOUT_MS, REPO_REFRESH_POLL_INTERVAL_MS = 500, REPO_REFRESH_POLL_TIMEOUT_MS = 1e4;
7466
+ var INSTALLATION_TIMEOUT_MS, REPO_REFRESH_POLL_INTERVAL_MS = 500, REPO_REFRESH_POLL_TIMEOUT_MS = 1e4, REPO_MULTISELECT_MAX_ITEMS = 10, DATA_SOURCE_VERIFY_POLL_INTERVAL_MS = 1000, DATA_SOURCE_VERIFY_POLL_TIMEOUT_MS = 1e4;
7386
7467
  var init_github_step = __esm(() => {
7387
7468
  init_dist6();
7388
7469
  init_trpc();
@@ -7771,7 +7852,7 @@ async function stepImportGitHubDocs(cfg, opts = {}) {
7771
7852
  return { advance: false };
7772
7853
  }
7773
7854
  const trpc = createTypedClient(cfg);
7774
- const files = opts.waitForFreshDocs ? await waitForImportableGithubFiles(trpc, cfg.org_id, cfg.space_id) : await fetchImportableGithubFiles(trpc, cfg.space_id);
7855
+ const files = opts.waitForFreshDocs ? await waitForImportableGithubFiles(trpc, cfg.org_id, cfg.space_id, opts.expectedDataSourceIds) : await fetchImportableGithubFiles(trpc, cfg.space_id);
7775
7856
  if (files === null) {
7776
7857
  return { advance: false };
7777
7858
  }
@@ -7891,7 +7972,7 @@ Your GitHub docs are ready, and onboarding is complete.`);
7891
7972
  M2.warn(`Imported ${processed - failed} of ${total} doc${total === 1 ? "" : "s"}; ${failed} failed.
7892
7973
  You can review the failed docs later, but onboarding is complete.`);
7893
7974
  }
7894
- async function waitForImportableGithubFiles(trpc, orgID, spaceID) {
7975
+ async function waitForImportableGithubFiles(trpc, orgID, spaceID, expectedDataSourceIds) {
7895
7976
  while (true) {
7896
7977
  const spinner = Y2();
7897
7978
  spinner.start("Scanning repositories for markdown documents...");
@@ -7905,7 +7986,7 @@ async function waitForImportableGithubFiles(trpc, orgID, spaceID) {
7905
7986
  return latestFiles;
7906
7987
  }
7907
7988
  const dataSources = await fetchGitHubDataSources(trpc, orgID);
7908
- if (dataSources.length > 0 && dataSources.every((ds) => ds.is_indexed === true)) {
7989
+ if (isScanComplete(dataSources, expectedDataSourceIds)) {
7909
7990
  spinner.stop("No markdown docs found");
7910
7991
  return [];
7911
7992
  }
@@ -7928,6 +8009,24 @@ async function waitForImportableGithubFiles(trpc, orgID, spaceID) {
7928
8009
  }
7929
8010
  }
7930
8011
  }
8012
+ function isScanComplete(dataSources, expectedDataSourceIds) {
8013
+ if (expectedDataSourceIds && expectedDataSourceIds.length > 0) {
8014
+ const byId = new Map;
8015
+ for (const ds of dataSources) {
8016
+ if (ds.data_source_id)
8017
+ byId.set(ds.data_source_id, ds);
8018
+ }
8019
+ for (const id of expectedDataSourceIds) {
8020
+ const ds = byId.get(id);
8021
+ if (!ds)
8022
+ continue;
8023
+ if (ds.is_indexed !== true)
8024
+ return false;
8025
+ }
8026
+ return true;
8027
+ }
8028
+ return dataSources.length > 0 && dataSources.every((ds) => ds.is_indexed === true);
8029
+ }
7931
8030
  async function fetchGitHubDataSources(trpc, orgID) {
7932
8031
  try {
7933
8032
  const dataSources = await trpc.dataSource.list.query({
@@ -8275,9 +8374,14 @@ async function runSetup(opts = {}) {
8275
8374
  const apiClient = new Client(cfg);
8276
8375
  let cloudSetupContext = null;
8277
8376
  if (cfg.mode !== MODE_OSS) {
8377
+ const s = Y2();
8378
+ s.start("Loading your workspace...");
8278
8379
  cloudSetupContext = await resolveCloudSetupContext(cfg);
8279
- if (!cloudSetupContext)
8380
+ if (!cloudSetupContext) {
8381
+ s.stop("Workspace load failed");
8280
8382
  return;
8383
+ }
8384
+ s.stop("Workspace loaded");
8281
8385
  }
8282
8386
  if (cfg.mode !== MODE_OSS && cloudSetupContext?.kind === "onboarding") {
8283
8387
  const ok = await bindOnboardingDeployment(apiClient, cfg, cloudSetupContext.targetOrg ?? null);
@@ -8320,7 +8424,8 @@ async function runSetup(opts = {}) {
8320
8424
  }
8321
8425
  const { stepImportGitHubDocs: stepImportGitHubDocs2 } = await Promise.resolve().then(() => (init_github_doc_import_step(), exports_github_doc_import_step));
8322
8426
  const importResult = await stepImportGitHubDocs2(cfg, {
8323
- waitForFreshDocs: Boolean(connectResult.deployment_id)
8427
+ waitForFreshDocs: Boolean(connectResult.deployment_id),
8428
+ expectedDataSourceIds: connectResult.created_data_source_ids
8324
8429
  });
8325
8430
  if (!importResult.advance)
8326
8431
  return;
@@ -8537,6 +8642,8 @@ async function bindOnboardingDeployment(apiClient, cfg, targetOrg) {
8537
8642
  cfg.org_id = deployment.org_id;
8538
8643
  cfg.space_id = deployment.space_id;
8539
8644
  logger.info("setup", `Bound onboarding context org=${targetOrg.org_id} deployment=${deployment.deployment_id}`);
8645
+ M2.success(`Organization
8646
+ ${dim(targetOrg.name)}`);
8540
8647
  return true;
8541
8648
  }
8542
8649
  async function resolveOnboardingDeployment(apiClient, targetOrg) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.11.0-alpha.3",
3
+ "version": "0.11.0-alpha.4",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",