@dosu/cli 0.11.0-alpha.2 → 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 +129 -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.2";
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) {
@@ -7291,19 +7300,65 @@ async function createDeploymentForRepo(trpc, orgID, spaceID, repo) {
7291
7300
  logger.warn("setup", `dataSource.create returned no data_source for ${repo.slug}`);
7292
7301
  return { deployment_id: deployment.deployment_id };
7293
7302
  }
7303
+ await trpc.dataSource.syncDataSource.mutate(dataSource.data_source_id);
7294
7304
  const spaceDeployments = await trpc.workspaces.listForSpace.query(spaceID);
7295
7305
  await Promise.all(spaceDeployments.map((d3) => trpc.deploymentDataSource.create.mutate({
7296
7306
  deployment_id: d3.deployment_id,
7297
7307
  data_source_id: dataSource.data_source_id
7298
7308
  })));
7299
- return { deployment_id: deployment.deployment_id };
7309
+ return {
7310
+ deployment_id: deployment.deployment_id,
7311
+ data_source_id: dataSource.data_source_id
7312
+ };
7300
7313
  } catch (err) {
7301
7314
  const msg = err instanceof Error ? err.message : String(err);
7302
7315
  logger.error("setup", `Failed to wire up ${repo.slug}: ${msg}`);
7303
7316
  return null;
7304
7317
  }
7305
7318
  }
7306
- 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 = {}) {
7307
7362
  logger.info("setup", "Step: connect GitHub repo(s)");
7308
7363
  if (!cfg.org_id || !cfg.space_id) {
7309
7364
  M2.warn("Cannot connect GitHub: your Dosu workspace is missing org/space context. Re-run `dosu setup` from a fresh state.");
@@ -7326,9 +7381,10 @@ async function stepConnectGitHubRepo(cfg, detected = detectGitRepo()) {
7326
7381
  ${lines}`);
7327
7382
  }
7328
7383
  const selected = await promptGitHubRepositories({
7329
- message: "Select repositories to connect",
7384
+ message: `Select repositories to connect ${dim(`(${undeployed.length} available)`)}`,
7330
7385
  options: buildPromptOptions(undeployed),
7331
- initialValues: []
7386
+ initialValues: [],
7387
+ maxItems: REPO_MULTISELECT_MAX_ITEMS
7332
7388
  });
7333
7389
  if (pD(selected)) {
7334
7390
  logger.info("setup", "Repository selection cancelled");
@@ -7359,29 +7415,55 @@ ${lines}`);
7359
7415
  continue;
7360
7416
  const result = await createDeploymentForRepo(trpc, orgID, spaceID, repo);
7361
7417
  if (result) {
7362
- 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 });
7363
7433
  }
7364
7434
  }
7365
- 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) {
7366
7437
  s.stop("Failed");
7367
- 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
+ }
7368
7443
  return { advance: false, has_connected_repo: deployed.length > 0 };
7369
7444
  }
7370
- s.stop(`Connected ${created.length} repo${created.length === 1 ? "" : "s"}`);
7371
- 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) {
7372
7452
  M2.success(`${slug}
7373
7453
  ${dim(`deployment ${deployment_id}`)}`);
7374
7454
  }
7375
- 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));
7376
7457
  return {
7377
7458
  advance: true,
7378
7459
  has_connected_repo: true,
7379
7460
  deployment_id: primary.deployment_id,
7380
- space_id: cfg.space_id
7461
+ space_id: cfg.space_id,
7462
+ created_data_source_ids: survivingDataSourceIds
7381
7463
  };
7382
7464
  }
7383
7465
  }
7384
- 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;
7385
7467
  var init_github_step = __esm(() => {
7386
7468
  init_dist6();
7387
7469
  init_trpc();
@@ -7770,7 +7852,7 @@ async function stepImportGitHubDocs(cfg, opts = {}) {
7770
7852
  return { advance: false };
7771
7853
  }
7772
7854
  const trpc = createTypedClient(cfg);
7773
- 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);
7774
7856
  if (files === null) {
7775
7857
  return { advance: false };
7776
7858
  }
@@ -7890,7 +7972,7 @@ Your GitHub docs are ready, and onboarding is complete.`);
7890
7972
  M2.warn(`Imported ${processed - failed} of ${total} doc${total === 1 ? "" : "s"}; ${failed} failed.
7891
7973
  You can review the failed docs later, but onboarding is complete.`);
7892
7974
  }
7893
- async function waitForImportableGithubFiles(trpc, orgID, spaceID) {
7975
+ async function waitForImportableGithubFiles(trpc, orgID, spaceID, expectedDataSourceIds) {
7894
7976
  while (true) {
7895
7977
  const spinner = Y2();
7896
7978
  spinner.start("Scanning repositories for markdown documents...");
@@ -7904,7 +7986,7 @@ async function waitForImportableGithubFiles(trpc, orgID, spaceID) {
7904
7986
  return latestFiles;
7905
7987
  }
7906
7988
  const dataSources = await fetchGitHubDataSources(trpc, orgID);
7907
- if (dataSources.length > 0 && dataSources.every((ds) => ds.is_indexed === true)) {
7989
+ if (isScanComplete(dataSources, expectedDataSourceIds)) {
7908
7990
  spinner.stop("No markdown docs found");
7909
7991
  return [];
7910
7992
  }
@@ -7927,6 +8009,24 @@ async function waitForImportableGithubFiles(trpc, orgID, spaceID) {
7927
8009
  }
7928
8010
  }
7929
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
+ }
7930
8030
  async function fetchGitHubDataSources(trpc, orgID) {
7931
8031
  try {
7932
8032
  const dataSources = await trpc.dataSource.list.query({
@@ -8274,9 +8374,14 @@ async function runSetup(opts = {}) {
8274
8374
  const apiClient = new Client(cfg);
8275
8375
  let cloudSetupContext = null;
8276
8376
  if (cfg.mode !== MODE_OSS) {
8377
+ const s = Y2();
8378
+ s.start("Loading your workspace...");
8277
8379
  cloudSetupContext = await resolveCloudSetupContext(cfg);
8278
- if (!cloudSetupContext)
8380
+ if (!cloudSetupContext) {
8381
+ s.stop("Workspace load failed");
8279
8382
  return;
8383
+ }
8384
+ s.stop("Workspace loaded");
8280
8385
  }
8281
8386
  if (cfg.mode !== MODE_OSS && cloudSetupContext?.kind === "onboarding") {
8282
8387
  const ok = await bindOnboardingDeployment(apiClient, cfg, cloudSetupContext.targetOrg ?? null);
@@ -8319,7 +8424,8 @@ async function runSetup(opts = {}) {
8319
8424
  }
8320
8425
  const { stepImportGitHubDocs: stepImportGitHubDocs2 } = await Promise.resolve().then(() => (init_github_doc_import_step(), exports_github_doc_import_step));
8321
8426
  const importResult = await stepImportGitHubDocs2(cfg, {
8322
- waitForFreshDocs: Boolean(connectResult.deployment_id)
8427
+ waitForFreshDocs: Boolean(connectResult.deployment_id),
8428
+ expectedDataSourceIds: connectResult.created_data_source_ids
8323
8429
  });
8324
8430
  if (!importResult.advance)
8325
8431
  return;
@@ -8536,6 +8642,8 @@ async function bindOnboardingDeployment(apiClient, cfg, targetOrg) {
8536
8642
  cfg.org_id = deployment.org_id;
8537
8643
  cfg.space_id = deployment.space_id;
8538
8644
  logger.info("setup", `Bound onboarding context org=${targetOrg.org_id} deployment=${deployment.deployment_id}`);
8645
+ M2.success(`Organization
8646
+ ${dim(targetOrg.name)}`);
8539
8647
  return true;
8540
8648
  }
8541
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.2",
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",