@codeagentswarm/cas-cloud 0.0.17 → 0.0.18

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.
@@ -1068,7 +1068,7 @@ var require_database_mcp_standalone = __commonJS({
1068
1068
  { status_key: "needs_testing", label: "Needs testing", color: "#3b82f6", icon: "flask-conical", sort_order: 2, is_default: 1, agent_settable: 1, prompt: "Set it when you finish the implementation and the work is pending the user testing it manually. Do not set it if there are still things left to implement." },
1069
1069
  { status_key: "working", label: "Working", color: "#fbbf24", icon: "hammer", sort_order: 3, is_default: 1, agent_settable: 1, prompt: "Set it when you start working on any request and while you are implementing, investigating or fixing something." },
1070
1070
  { status_key: "done", label: "Done", color: "#22c55e", icon: "circle-check", sort_order: 4, is_default: 1, agent_settable: 1, prompt: "Set it when the work is completely finished: implemented, validated and with its commit/push done when applicable. It is the final state." },
1071
- { status_key: "idle", label: "Idle", color: "#6b7280", icon: "circle-dashed", sort_order: 5, is_default: 1, agent_settable: 0, prompt: "Set by the app when an agent is resting without an explicit work status, including after a reply finishes. It does not mean the work is complete or that user input is required. Agents cannot set this status." }
1071
+ { status_key: "idle", label: "Idle", color: "#6b7280", icon: "circle-dashed", sort_order: 5, is_default: 1, agent_settable: 0, prompt: "Set by the app on an agent that has just been opened and has not been given any work yet. Agents cannot set this status; it clears itself as soon as you send the agent something." }
1072
1072
  ];
1073
1073
  var IDLE_TERMINAL_STATUS_KEY = "idle";
1074
1074
  var IDLE_STATUS_SEEDED_SETTING = "terminal_status_idle_seeded";
@@ -1081,7 +1081,7 @@ var require_database_mcp_standalone = __commonJS({
1081
1081
  done: 6
1082
1082
  };
1083
1083
  var LEGACY_DEFAULT_PROMPTS = {
1084
- idle: "Set by the app on an agent that has just been opened and has not been given any work yet. Agents cannot set this status; it clears itself as soon as you send the agent something.",
1084
+ idle: "Set by the app when an agent is resting without an explicit work status, including after a reply finishes. It does not mean the work is complete or that user input is required. Agents cannot set this status.",
1085
1085
  working: "Ponlo al empezar a trabajar en cualquier petici\xF3n y mientras est\xE9s implementando, investigando o arreglando algo.",
1086
1086
  needs_input: "Ponlo cuando pares porque necesites una respuesta o decisi\xF3n del usuario para continuar (una pregunta, una elecci\xF3n de dise\xF1o, un permiso).",
1087
1087
  needs_testing: "Ponlo cuando termines la implementaci\xF3n y el trabajo quede pendiente de que el usuario lo pruebe a mano. No lo pongas si a\xFAn quedan cosas por implementar.",
package/dist/cas.js CHANGED
@@ -897,13 +897,14 @@ var require_provider_login_manager = __commonJS({
897
897
  }
898
898
  }
899
899
  if (agent === "codex") {
900
- const signedOut = /\bnot\s+(?:logged|signed)\s+in\b/i.test(output);
900
+ const signedOut2 = /\bnot\s+(?:logged|signed)\s+in\b/i.test(output);
901
901
  return {
902
- loggedIn: !signedOut && /\b(?:logged|signed)\s+in\b/i.test(output),
902
+ loggedIn: !signedOut2 && /\b(?:logged|signed)\s+in\b/i.test(output),
903
903
  detail: output.split("\n")[0] || ""
904
904
  };
905
905
  }
906
- return { loggedIn: code === 0, detail: output.split("\n")[0] || "" };
906
+ const signedOut = agent === "cursor" && /\bnot\s+(?:logged|signed)\s+in\b/i.test(output);
907
+ return { loggedIn: code === 0 && !signedOut, detail: output.split("\n")[0] || "" };
907
908
  }
908
909
  function safeRequireRegistry() {
909
910
  try {
@@ -7574,7 +7575,7 @@ var require_codex_quota_reader = __commonJS({
7574
7575
  * accountWide: { snapshot: object, accountWide: boolean }|null
7575
7576
  * }|null>}
7576
7577
  */
7577
- async readCandidatesFromFile(filePath, knownSessionId = null) {
7578
+ async readCandidatesFromFile(filePath, knownSessionId = null, { since = null } = {}) {
7578
7579
  var _a;
7579
7580
  try {
7580
7581
  if (!filePath || !fs.existsSync(filePath)) return null;
@@ -7602,6 +7603,9 @@ var require_codex_quota_reader = __commonJS({
7602
7603
  const parsed = JSON.parse(line.toString("utf8"));
7603
7604
  const rateLimits = extractRateLimits(parsed);
7604
7605
  if (!rateLimits) return false;
7606
+ if (since && parsed.timestamp && Date.parse(parsed.timestamp) < since) {
7607
+ return true;
7608
+ }
7605
7609
  const windows = [];
7606
7610
  const primary = bucketToWindow(rateLimits.primary);
7607
7611
  if (primary) windows.push(primary);
@@ -7733,7 +7737,7 @@ var require_codex_quota_reader = __commonJS({
7733
7737
  return null;
7734
7738
  }
7735
7739
  }
7736
- async getQuotas(accountForSession, activeBindings = null) {
7740
+ async getQuotas(accountForSession, activeBindings = null, bindingSince = null) {
7737
7741
  if (typeof accountForSession !== "function") {
7738
7742
  const snapshot = await this.getQuota();
7739
7743
  return snapshot ? [snapshot] : [];
@@ -7761,7 +7765,10 @@ var require_codex_quota_reader = __commonJS({
7761
7765
  for (const [accountId, files] of boundFilesByAccount) {
7762
7766
  let fallback = null;
7763
7767
  for (const { file, sessionId } of files) {
7764
- const candidates = await this.readCandidatesFromFile(file, sessionId);
7768
+ const since = bindingSince && typeof bindingSince === "object" ? bindingSince[sessionId] : null;
7769
+ const candidates = await this.readCandidatesFromFile(file, sessionId, {
7770
+ since: Number.isFinite(since) ? since : null
7771
+ });
7765
7772
  if (!candidates) continue;
7766
7773
  if (candidates.accountWide && hasActiveWindow(candidates.accountWide.snapshot)) {
7767
7774
  boundSnapshots.push({ ...candidates.accountWide.snapshot, accountId });
@@ -8000,16 +8007,18 @@ var require_codex_cli_strategy = __commonJS({
8000
8007
  async getQuota() {
8001
8008
  return getCodexQuotaReader().getInstance().getQuota();
8002
8009
  }
8003
- setQuotaAccountResolver(resolver, isEnabled = null, activeBindings = null) {
8010
+ setQuotaAccountResolver(resolver, isEnabled = null, activeBindings = null, bindingSince = null) {
8004
8011
  this._quotaAccountResolver = typeof resolver === "function" ? resolver : null;
8005
8012
  this._quotaAccountResolverEnabled = typeof isEnabled === "function" ? isEnabled : null;
8006
8013
  this._quotaAccountBindings = typeof activeBindings === "function" ? activeBindings : null;
8014
+ this._quotaAccountBindingSince = typeof bindingSince === "function" ? bindingSince : null;
8007
8015
  }
8008
8016
  async getQuotas() {
8009
- var _a, _b;
8017
+ var _a, _b, _c;
8010
8018
  const resolver = ((_a = this._quotaAccountResolverEnabled) == null ? void 0 : _a.call(this)) === false ? null : this._quotaAccountResolver;
8011
8019
  const bindings = resolver ? (_b = this._quotaAccountBindings) == null ? void 0 : _b.call(this) : null;
8012
- return getCodexQuotaReader().getInstance().getQuotas(resolver, bindings);
8020
+ const bindingSince = (resolver ? (_c = this._quotaAccountBindingSince) == null ? void 0 : _c.call(this) : null) ?? null;
8021
+ return getCodexQuotaReader().getInstance().getQuotas(resolver, bindings, bindingSince);
8013
8022
  }
8014
8023
  // ========================================
8015
8024
  // Detection Methods
@@ -27667,7 +27676,7 @@ var require_database_mcp_standalone = __commonJS({
27667
27676
  { status_key: "needs_testing", label: "Needs testing", color: "#3b82f6", icon: "flask-conical", sort_order: 2, is_default: 1, agent_settable: 1, prompt: "Set it when you finish the implementation and the work is pending the user testing it manually. Do not set it if there are still things left to implement." },
27668
27677
  { status_key: "working", label: "Working", color: "#fbbf24", icon: "hammer", sort_order: 3, is_default: 1, agent_settable: 1, prompt: "Set it when you start working on any request and while you are implementing, investigating or fixing something." },
27669
27678
  { status_key: "done", label: "Done", color: "#22c55e", icon: "circle-check", sort_order: 4, is_default: 1, agent_settable: 1, prompt: "Set it when the work is completely finished: implemented, validated and with its commit/push done when applicable. It is the final state." },
27670
- { status_key: "idle", label: "Idle", color: "#6b7280", icon: "circle-dashed", sort_order: 5, is_default: 1, agent_settable: 0, prompt: "Set by the app when an agent is resting without an explicit work status, including after a reply finishes. It does not mean the work is complete or that user input is required. Agents cannot set this status." }
27679
+ { status_key: "idle", label: "Idle", color: "#6b7280", icon: "circle-dashed", sort_order: 5, is_default: 1, agent_settable: 0, prompt: "Set by the app on an agent that has just been opened and has not been given any work yet. Agents cannot set this status; it clears itself as soon as you send the agent something." }
27671
27680
  ];
27672
27681
  var IDLE_TERMINAL_STATUS_KEY = "idle";
27673
27682
  var IDLE_STATUS_SEEDED_SETTING = "terminal_status_idle_seeded";
@@ -27680,7 +27689,7 @@ var require_database_mcp_standalone = __commonJS({
27680
27689
  done: 6
27681
27690
  };
27682
27691
  var LEGACY_DEFAULT_PROMPTS = {
27683
- idle: "Set by the app on an agent that has just been opened and has not been given any work yet. Agents cannot set this status; it clears itself as soon as you send the agent something.",
27692
+ idle: "Set by the app when an agent is resting without an explicit work status, including after a reply finishes. It does not mean the work is complete or that user input is required. Agents cannot set this status.",
27684
27693
  working: "Ponlo al empezar a trabajar en cualquier petici\xF3n y mientras est\xE9s implementando, investigando o arreglando algo.",
27685
27694
  needs_input: "Ponlo cuando pares porque necesites una respuesta o decisi\xF3n del usuario para continuar (una pregunta, una elecci\xF3n de dise\xF1o, un permiso).",
27686
27695
  needs_testing: "Ponlo cuando termines la implementaci\xF3n y el trabajo quede pendiente de que el usuario lo pruebe a mano. No lo pongas si a\xFAn quedan cosas por implementar.",
@@ -35313,9 +35322,12 @@ var require_mobile_runtime = __commonJS({
35313
35322
  workspaceGitCreate = null,
35314
35323
  listProjects = null,
35315
35324
  listProjectDirectories = null,
35325
+ listProjectLocations = null,
35326
+ addProjectLocation = null,
35316
35327
  createProject = null,
35317
35328
  updateProject = null,
35318
35329
  gitAvailability = null,
35330
+ listGitHubRepositories = null,
35319
35331
  projectIconAvailability = null,
35320
35332
  generateProjectIcon = null,
35321
35333
  registerProject = null,
@@ -35384,9 +35396,12 @@ var require_mobile_runtime = __commonJS({
35384
35396
  this.workspaceGitCreate = workspaceGitCreate;
35385
35397
  this.listProjects = listProjects;
35386
35398
  this.listProjectDirectories = listProjectDirectories;
35399
+ this.listProjectLocations = listProjectLocations;
35400
+ this.addProjectLocation = addProjectLocation;
35387
35401
  this.createProject = createProject;
35388
35402
  this.updateProject = updateProject;
35389
35403
  this.gitAvailability = gitAvailability;
35404
+ this.listGitHubRepositories = listGitHubRepositories;
35390
35405
  this.projectIconAvailability = projectIconAvailability;
35391
35406
  this.generateProjectIcon = generateProjectIcon;
35392
35407
  this.registerProject = registerProject;
@@ -36502,6 +36517,7 @@ var require_mobile_runtime = __commonJS({
36502
36517
  "tasks.list",
36503
36518
  "projects.list",
36504
36519
  "project.directories.list",
36520
+ "project.locations.list",
36505
36521
  "providers.list",
36506
36522
  "provider.login.describe",
36507
36523
  "workspace.files.list",
@@ -36667,6 +36683,19 @@ var require_mobile_runtime = __commonJS({
36667
36683
  relativePath: payload.relativePath
36668
36684
  });
36669
36685
  }
36686
+ if (["project.locations.list", "project.locations.add"].includes(command.type)) {
36687
+ const adding = command.type === "project.locations.add";
36688
+ exactPayload(adding ? ["locationId", "requestId"] : ["locationId", "offset"]);
36689
+ try {
36690
+ const result = adding ? await this.addProjectLocation({ locationId: payload.locationId, requestId: mutationRequestId() }) : await this.listProjectLocations(payload);
36691
+ if (adding) this.publishProjects();
36692
+ return result;
36693
+ } catch (error) {
36694
+ throw Object.assign(new Error("The remote location could not be read or saved."), {
36695
+ code: ["location_expired", "location_permission_denied"].includes(error.code) ? error.code : "location_unavailable"
36696
+ });
36697
+ }
36698
+ }
36670
36699
  if (command.type === "project.create") {
36671
36700
  if (typeof this.createProject !== "function") throw new Error("Remote project creation is unavailable");
36672
36701
  exactPayload(["name", "projectPath", "color", "icon", "requestId"]);
@@ -36686,6 +36715,11 @@ var require_mobile_runtime = __commonJS({
36686
36715
  exactPayload(["rootId"]);
36687
36716
  return this.gitAvailability({ rootId: cleanText(payload.rootId, 128) });
36688
36717
  }
36718
+ if (command.type === "project.github.repositories") {
36719
+ exactPayload(["rootId"]);
36720
+ if (typeof this.listGitHubRepositories !== "function") throw new Error("Remote GitHub import is unavailable");
36721
+ return this.listGitHubRepositories();
36722
+ }
36689
36723
  if (command.type === "project.icon.availability") {
36690
36724
  if (typeof this.projectIconAvailability !== "function") return { available: false };
36691
36725
  exactPayload(["projectId"]);
@@ -36709,11 +36743,12 @@ var require_mobile_runtime = __commonJS({
36709
36743
  }
36710
36744
  if (command.type === "project.clone") {
36711
36745
  if (typeof this.cloneProject !== "function") throw new Error("Remote project cloning is unavailable");
36712
- exactPayload(["rootId", "url", "relativePath", "displayName", "color", "icon", "requestId"]);
36746
+ exactPayload(["rootId", "url", "relativePath", "displayName", "color", "icon", "requestId", "githubRepository"]);
36713
36747
  if (typeof payload.requestId !== "string" || !payload.requestId) throw new Error("A clone requestId is required");
36714
36748
  return this.cloneProject({
36715
36749
  rootId: payload.rootId,
36716
36750
  url: payload.url,
36751
+ ...payload.githubRepository !== void 0 ? { githubRepository: payload.githubRepository } : {},
36717
36752
  relativePath: payload.relativePath,
36718
36753
  displayName: payload.displayName,
36719
36754
  color: payload.color,
@@ -38731,6 +38766,9 @@ var require_remote_runtime_client = __commonJS({
38731
38766
  "project.clone",
38732
38767
  "project.clone.cancel",
38733
38768
  "project.git.availability",
38769
+ "project.github.repositories",
38770
+ "project.locations.list",
38771
+ "project.locations.add",
38734
38772
  "project.icon.availability",
38735
38773
  "project.icon.generate",
38736
38774
  "project.unregister",
@@ -41539,6 +41577,107 @@ var require_headless_chat_preferences = __commonJS({
41539
41577
  }
41540
41578
  });
41541
41579
 
41580
+ // src/infrastructure/services/remote-project-locations.js
41581
+ var require_remote_project_locations = __commonJS({
41582
+ "src/infrastructure/services/remote-project-locations.js"(exports2, module2) {
41583
+ "use strict";
41584
+ var crypto = require("crypto");
41585
+ var fs = require("fs");
41586
+ var os = require("os");
41587
+ var path = require("path");
41588
+ function locationError(code, message) {
41589
+ return Object.assign(new Error(message), { code });
41590
+ }
41591
+ var RemoteProjectLocations = class {
41592
+ constructor() {
41593
+ this.locations = /* @__PURE__ */ new Map();
41594
+ }
41595
+ remember(candidate) {
41596
+ var _a;
41597
+ const resolved = fs.realpathSync(candidate);
41598
+ const stat = fs.statSync(resolved);
41599
+ if (!stat.isDirectory()) throw new Error("Choose an existing folder");
41600
+ let locationId = (_a = [...this.locations].find(([, entry]) => entry.path === resolved)) == null ? void 0 : _a[0];
41601
+ const previous = this.locations.get(locationId);
41602
+ if (previous && (previous.dev !== stat.dev || previous.ino !== stat.ino)) {
41603
+ this.locations.delete(locationId);
41604
+ locationId = null;
41605
+ }
41606
+ if (!locationId) {
41607
+ if (this.locations.size >= 4096) this.locations.delete(this.locations.keys().next().value);
41608
+ locationId = crypto.randomBytes(18).toString("base64url");
41609
+ this.locations.set(locationId, { path: resolved, dev: stat.dev, ino: stat.ino });
41610
+ }
41611
+ return { locationId, name: path.basename(resolved) || path.parse(resolved).root };
41612
+ }
41613
+ resolve(locationId) {
41614
+ const entry = this.locations.get(locationId);
41615
+ if (!entry) throw locationError("location_expired", "This folder selection expired. Browse again.");
41616
+ try {
41617
+ const stat = fs.lstatSync(entry.path);
41618
+ if (!stat.isDirectory() || fs.realpathSync(entry.path) !== entry.path || stat.dev !== entry.dev || stat.ino !== entry.ino) throw new Error();
41619
+ } catch (_) {
41620
+ throw locationError("location_expired", "The folder changed. Browse again.");
41621
+ }
41622
+ return entry.path;
41623
+ }
41624
+ writable(locationId) {
41625
+ const resolved = this.resolve(locationId);
41626
+ const stat = fs.statSync(resolved);
41627
+ const uid = typeof process.geteuid === "function" ? process.geteuid() : null;
41628
+ if (resolved === path.parse(resolved).root || uid !== null && (stat.uid !== uid || (stat.mode & 18) !== 0)) {
41629
+ throw locationError("location_permission_denied", "Choose a private folder owned by the remote service user, not a whole drive.");
41630
+ }
41631
+ try {
41632
+ fs.accessSync(resolved, fs.constants.R_OK | fs.constants.W_OK | fs.constants.X_OK);
41633
+ } catch (_) {
41634
+ throw locationError("location_permission_denied", "The remote service cannot read and write this folder. Choose another location.");
41635
+ }
41636
+ return resolved;
41637
+ }
41638
+ list({ locationId, offset = 0 } = {}, roots = []) {
41639
+ var _a;
41640
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset > 1e5) throw new Error("Invalid folder page");
41641
+ const shortcuts = [os.homedir(), ...roots];
41642
+ if (process.platform === "win32") {
41643
+ for (const letter of "ABCDEFGHIJKLMNOPQRSTUVWXYZ") if (fs.existsSync(`${letter}:\\`)) shortcuts.push(`${letter}:\\`);
41644
+ } else shortcuts.push("/");
41645
+ const locations = [...new Set(shortcuts)].flatMap((candidate) => {
41646
+ try {
41647
+ return [this.remember(candidate)];
41648
+ } catch (_) {
41649
+ return [];
41650
+ }
41651
+ });
41652
+ const current = locationId ? this.resolve(locationId) : this.resolve((_a = locations[0]) == null ? void 0 : _a.locationId);
41653
+ let entries;
41654
+ try {
41655
+ entries = fs.readdirSync(current, { withFileTypes: true });
41656
+ } catch (_) {
41657
+ throw locationError("location_permission_denied", "The remote service cannot read this folder. Choose another location.");
41658
+ }
41659
+ const folders = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).sort((left, right) => left.name.localeCompare(right.name, void 0, { numeric: true }));
41660
+ const directories = folders.slice(offset, offset + 200).flatMap((entry) => {
41661
+ try {
41662
+ return [this.remember(path.join(current, entry.name))];
41663
+ } catch (_) {
41664
+ return [];
41665
+ }
41666
+ });
41667
+ const parent = path.dirname(current);
41668
+ return {
41669
+ ...this.remember(current),
41670
+ parentLocationId: parent === current ? null : this.remember(parent).locationId,
41671
+ locations,
41672
+ directories,
41673
+ nextOffset: offset + 200 < folders.length ? offset + 200 : null
41674
+ };
41675
+ }
41676
+ };
41677
+ module2.exports = { RemoteProjectLocations };
41678
+ }
41679
+ });
41680
+
41542
41681
  // src/infrastructure/headless/headless-project-registry.js
41543
41682
  var require_headless_project_registry = __commonJS({
41544
41683
  "src/infrastructure/headless/headless-project-registry.js"(exports2, module2) {
@@ -41546,6 +41685,7 @@ var require_headless_project_registry = __commonJS({
41546
41685
  var fs = require("fs");
41547
41686
  var path = require("path");
41548
41687
  var { spawn } = require("child_process");
41688
+ var { RemoteProjectLocations } = require_remote_project_locations();
41549
41689
  var ID_PATTERN = /^[A-Za-z0-9_-]{16,128}$/;
41550
41690
  var MAX_CLONES = 2;
41551
41691
  var MAX_QUEUE = 20;
@@ -41639,6 +41779,7 @@ var require_headless_project_registry = __commonJS({
41639
41779
  this.database = database;
41640
41780
  this.db = database.db;
41641
41781
  this.runtimeId = runtimeId;
41782
+ this.locations = new RemoteProjectLocations();
41642
41783
  this.spawnImpl = spawnImpl;
41643
41784
  this.onProjectsChanged = onProjectsChanged;
41644
41785
  this.onOperation = onOperation;
@@ -41884,6 +42025,20 @@ var require_headless_project_registry = __commonJS({
41884
42025
  getRoots() {
41885
42026
  return this.db.prepare("SELECT root_id, name FROM runtime_project_roots ORDER BY created_at, root_id").all().map((root) => ({ rootId: root.root_id, name: String(root.name).slice(0, 200) }));
41886
42027
  }
42028
+ listLocations(payload) {
42029
+ return this.locations.list(payload, this.db.prepare("SELECT path FROM runtime_project_roots").all().map((root) => root.path));
42030
+ }
42031
+ addLocation({ locationId }) {
42032
+ const resolved = this.locations.writable(locationId);
42033
+ const existing = this.db.prepare("SELECT root_id FROM runtime_project_roots WHERE path = ?").get(resolved);
42034
+ if (existing) return { rootId: existing.root_id, name: path.basename(resolved) };
42035
+ if (this.getRoots().length >= 100) throw new Error("At most 100 project locations can be saved");
42036
+ const rootId = randomId();
42037
+ const name = path.basename(resolved);
42038
+ this.db.prepare("INSERT INTO runtime_project_roots (root_id, path, name) VALUES (?, ?, ?)").run(rootId, resolved, name);
42039
+ this._bumpRevision();
42040
+ return { rootId, name };
42041
+ }
41887
42042
  gitAvailability() {
41888
42043
  return new Promise((resolve) => {
41889
42044
  var _a, _b;
@@ -42083,7 +42238,7 @@ var require_headless_project_registry = __commonJS({
42083
42238
  const revision = this._bumpRevision();
42084
42239
  return this._recordRequest(requestId, hash, { projectId: project.projectId, revision, updated: true });
42085
42240
  }
42086
- clone({ rootId, url, relativePath, displayName, color, icon, requestId }) {
42241
+ clone({ rootId, url, relativePath, displayName, color, icon, requestId, gitConfig = [] }) {
42087
42242
  const normalizedUrl = validateGitUrl(url);
42088
42243
  const root = this._root(rootId);
42089
42244
  this._assertSecureCloneRoot(root);
@@ -42096,7 +42251,7 @@ var require_headless_project_registry = __commonJS({
42096
42251
  if (appearance.displayName !== void 0 && !appearance.displayName || appearance.icon !== void 0 && appearance.icon !== null && (typeof appearance.icon !== "string" || !ICON_PATTERN.test(appearance.icon))) {
42097
42252
  throw runtimeError("invalid_project_update", "Project changes are invalid");
42098
42253
  }
42099
- const hash = requestHash("clone", { rootId, url: normalizedUrl, relativePath: normalizedRelative, ...appearance });
42254
+ const hash = requestHash("clone", { rootId, url: normalizedUrl, relativePath: normalizedRelative, ...appearance, ...gitConfig.length ? { gitConfig } : {} });
42100
42255
  const duplicate = this._request(requestId, hash);
42101
42256
  if (duplicate) return duplicate;
42102
42257
  const destinationInfo = this._cloneDestination(root, normalizedRelative, normalizedUrl);
@@ -42113,6 +42268,7 @@ var require_headless_project_registry = __commonJS({
42113
42268
  requestId,
42114
42269
  rootId,
42115
42270
  url: normalizedUrl,
42271
+ gitConfig,
42116
42272
  appearance,
42117
42273
  destination: destinationInfo.destination,
42118
42274
  temporaryDestination: null,
@@ -42212,7 +42368,7 @@ var require_headless_project_registry = __commonJS({
42212
42368
  this._emitOperation(operation.operationId, operation.requestId, "running");
42213
42369
  let child;
42214
42370
  try {
42215
- child = this.spawnImpl("git", ["-c", "protocol.file.allow=never", "clone", "--", operation.url, operation.temporaryDestination], {
42371
+ child = this.spawnImpl("git", ["-c", "protocol.file.allow=never", ...operation.gitConfig, "clone", "--", operation.url, operation.temporaryDestination], {
42216
42372
  shell: false,
42217
42373
  stdio: ["ignore", "pipe", "pipe"],
42218
42374
  env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_ALLOW_PROTOCOL: "https:ssh" }
@@ -42350,6 +42506,272 @@ var require_headless_project_registry = __commonJS({
42350
42506
  }
42351
42507
  });
42352
42508
 
42509
+ // src/shared/utils/executable-candidate.js
42510
+ var require_executable_candidate = __commonJS({
42511
+ "src/shared/utils/executable-candidate.js"(exports2, module2) {
42512
+ var fs = require("fs");
42513
+ var WINDOWS_EXECUTABLE_EXTENSIONS = [".exe", ".cmd", ".bat"];
42514
+ function candidateVariants(candidate, platform = process.platform) {
42515
+ const value = String(candidate || "").trim();
42516
+ if (!value) return [];
42517
+ if (platform !== "win32" || /\.(exe|cmd|bat)$/i.test(value)) return [value];
42518
+ return WINDOWS_EXECUTABLE_EXTENSIONS.map((extension) => `${value}${extension}`);
42519
+ }
42520
+ function isRunnableExecutableCandidate(candidate, {
42521
+ fsImpl = fs,
42522
+ platform = process.platform
42523
+ } = {}) {
42524
+ const value = String(candidate || "").trim();
42525
+ if (!value) return false;
42526
+ if (platform === "win32" && !/\.(exe|cmd|bat)$/i.test(value)) return false;
42527
+ try {
42528
+ const stats = fsImpl.statSync(value);
42529
+ if (!stats.isFile() || stats.size <= 0) return false;
42530
+ if (platform !== "win32") fsImpl.accessSync(value, fs.constants.X_OK);
42531
+ return true;
42532
+ } catch (_) {
42533
+ return false;
42534
+ }
42535
+ }
42536
+ function findRunnableExecutableCandidate(candidates, options = {}) {
42537
+ const platform = options.platform || process.platform;
42538
+ for (const candidate of candidates || []) {
42539
+ for (const variant of candidateVariants(candidate, platform)) {
42540
+ if (isRunnableExecutableCandidate(variant, { ...options, platform })) return variant;
42541
+ }
42542
+ }
42543
+ return null;
42544
+ }
42545
+ module2.exports = {
42546
+ candidateVariants,
42547
+ findRunnableExecutableCandidate,
42548
+ isRunnableExecutableCandidate
42549
+ };
42550
+ }
42551
+ });
42552
+
42553
+ // src/infrastructure/services/github-project-import-service.js
42554
+ var require_github_project_import_service = __commonJS({
42555
+ "src/infrastructure/services/github-project-import-service.js"(exports2, module2) {
42556
+ "use strict";
42557
+ var fs = require("fs");
42558
+ var os = require("os");
42559
+ var path = require("path");
42560
+ var { execFile } = require("child_process");
42561
+ var { findRunnableExecutableCandidate } = require_executable_candidate();
42562
+ var REPOSITORY_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\/[A-Za-z0-9._-]+$/;
42563
+ var GitHubProjectImportService = class {
42564
+ constructor({
42565
+ execFileImpl = execFile,
42566
+ fsImpl = fs,
42567
+ osImpl = os,
42568
+ pathImpl = path,
42569
+ resolveEnv = async () => ({}),
42570
+ resolveExecutable = null
42571
+ } = {}) {
42572
+ this.execFile = execFileImpl;
42573
+ this.fs = fsImpl;
42574
+ this.os = osImpl;
42575
+ this.path = pathImpl;
42576
+ this.resolveEnv = resolveEnv;
42577
+ this.resolveExecutable = resolveExecutable;
42578
+ this.executablePath = null;
42579
+ }
42580
+ async _env() {
42581
+ const resolved = await this.resolveEnv();
42582
+ const env = { ...process.env };
42583
+ for (const [key, value] of Object.entries(resolved || {})) {
42584
+ for (const existingKey of Object.keys(env)) {
42585
+ if (existingKey !== key && existingKey.toUpperCase() === key.toUpperCase()) delete env[existingKey];
42586
+ }
42587
+ env[key] = value;
42588
+ }
42589
+ return env;
42590
+ }
42591
+ async _findExecutable(env) {
42592
+ if (this.executablePath) return this.executablePath;
42593
+ if (this.resolveExecutable) {
42594
+ this.executablePath = await this.resolveExecutable(env);
42595
+ return this.executablePath;
42596
+ }
42597
+ const pathValue = env.PATH || env.Path || "";
42598
+ const candidates = pathValue.split(this.path.delimiter).filter(Boolean).map((directory) => this.path.join(directory, "gh"));
42599
+ if (process.platform === "win32") {
42600
+ candidates.push("C:\\Program Files\\GitHub CLI\\gh.exe");
42601
+ }
42602
+ this.executablePath = findRunnableExecutableCandidate(candidates);
42603
+ return this.executablePath;
42604
+ }
42605
+ async _run(args, { timeout = 3e4, maxBuffer = 20 * 1024 * 1024 } = {}) {
42606
+ const env = await this._env();
42607
+ const executable = await this._findExecutable(env);
42608
+ if (!executable) {
42609
+ const error = new Error("GitHub CLI is not installed");
42610
+ error.code = "github_cli_missing";
42611
+ throw error;
42612
+ }
42613
+ return new Promise((resolve, reject) => {
42614
+ this.execFile(executable, args, { env, timeout, maxBuffer, windowsHide: true }, (error, stdout = "", stderr = "") => {
42615
+ if (error) {
42616
+ error.stdout = stdout;
42617
+ error.stderr = stderr;
42618
+ reject(error);
42619
+ return;
42620
+ }
42621
+ resolve({ stdout, stderr });
42622
+ });
42623
+ });
42624
+ }
42625
+ _defaultBaseDirectory() {
42626
+ const home = this.os.homedir();
42627
+ for (const candidate of [this.path.join(home, "Development"), this.path.join(home, "Developer"), home]) {
42628
+ try {
42629
+ if (this.fs.statSync(candidate).isDirectory()) return candidate;
42630
+ } catch (_) {
42631
+ }
42632
+ }
42633
+ return home;
42634
+ }
42635
+ _message(error) {
42636
+ return String((error == null ? void 0 : error.stderr) || (error == null ? void 0 : error.message) || "GitHub request failed").replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "").replace(/\s+/g, " ").trim().slice(0, 500);
42637
+ }
42638
+ async getStatus() {
42639
+ const env = await this._env();
42640
+ const executable = await this._findExecutable(env);
42641
+ const base = { defaultBaseDirectory: this._defaultBaseDirectory() };
42642
+ if (!executable) return { success: true, installed: false, authenticated: false, ...base };
42643
+ try {
42644
+ const { stdout } = await this._run(["api", "user"]);
42645
+ const user = JSON.parse(stdout);
42646
+ return {
42647
+ success: true,
42648
+ installed: true,
42649
+ authenticated: true,
42650
+ account: {
42651
+ login: user.login,
42652
+ name: user.name || user.login,
42653
+ avatarUrl: user.avatar_url || null
42654
+ },
42655
+ ...base
42656
+ };
42657
+ } catch (error) {
42658
+ return { success: true, installed: true, authenticated: false, error: this._message(error), ...base };
42659
+ }
42660
+ }
42661
+ async listRepositories() {
42662
+ try {
42663
+ const { stdout } = await this._run([
42664
+ "api",
42665
+ "--paginate",
42666
+ "--slurp",
42667
+ "user/repos?per_page=100&sort=updated&affiliation=owner,collaborator,organization_member"
42668
+ ]);
42669
+ const parsed = JSON.parse(stdout);
42670
+ const repositories = (Array.isArray(parsed[0]) ? parsed.flat() : parsed).filter((repo) => repo && repo.full_name && repo.name).map((repo) => {
42671
+ var _a;
42672
+ return {
42673
+ id: repo.id,
42674
+ name: repo.name,
42675
+ fullName: repo.full_name,
42676
+ description: repo.description || "",
42677
+ isPrivate: Boolean(repo.private),
42678
+ isFork: Boolean(repo.fork),
42679
+ updatedAt: repo.updated_at || null,
42680
+ language: repo.language || null,
42681
+ owner: ((_a = repo.owner) == null ? void 0 : _a.login) || repo.full_name.split("/")[0],
42682
+ url: repo.html_url || `https://github.com/${repo.full_name}`
42683
+ };
42684
+ });
42685
+ return { success: true, repositories };
42686
+ } catch (error) {
42687
+ return { success: false, error: this._message(error), code: error.code || "github_request_failed" };
42688
+ }
42689
+ }
42690
+ async connect() {
42691
+ try {
42692
+ await this._run(
42693
+ ["auth", "login", "--hostname", "github.com", "--git-protocol", "https", "--web"],
42694
+ { timeout: 10 * 6e4, maxBuffer: 1024 * 1024 }
42695
+ );
42696
+ return this.getStatus();
42697
+ } catch (error) {
42698
+ return { success: false, error: this._message(error), code: error.code || "github_auth_failed" };
42699
+ }
42700
+ }
42701
+ async remoteRepositories() {
42702
+ var _a;
42703
+ const status = await this.getStatus();
42704
+ const result = { success: true, installed: status.installed, authenticated: status.authenticated };
42705
+ if (!status.authenticated) return result;
42706
+ const listed = await this.listRepositories();
42707
+ if (!listed.success) return { success: false, error: "Could not load repositories from GitHub on the remote computer." };
42708
+ return {
42709
+ ...result,
42710
+ account: { login: String(((_a = status.account) == null ? void 0 : _a.login) || "").slice(0, 100) },
42711
+ repositories: listed.repositories.filter((repo) => typeof repo.fullName === "string" && repo.fullName.length <= 240 && REPOSITORY_PATTERN.test(repo.fullName)).slice(0, 500).map((repo) => ({
42712
+ name: repo.fullName.split("/")[1],
42713
+ fullName: repo.fullName,
42714
+ owner: repo.fullName.split("/")[0],
42715
+ description: String(repo.description || "").slice(0, 300),
42716
+ language: String(repo.language || "").slice(0, 80),
42717
+ isPrivate: repo.isPrivate === true
42718
+ })),
42719
+ truncated: listed.repositories.length > 500
42720
+ };
42721
+ }
42722
+ async prepareRemoteClone(payload) {
42723
+ if (payload.githubRepository === void 0) return payload;
42724
+ const repository = payload.githubRepository;
42725
+ if (typeof repository !== "string" || repository.length > 240 || !REPOSITORY_PATTERN.test(repository) || payload.url !== `https://github.com/${repository}.git`) {
42726
+ throw Object.assign(new Error("Choose a valid GitHub repository"), { code: "invalid_git_url" });
42727
+ }
42728
+ const executable = await this._findExecutable(await this._env());
42729
+ if (!executable) throw Object.assign(new Error("GitHub CLI is not installed on this computer"), { code: "github_cli_missing" });
42730
+ const quoted = `'${executable.replace(/\\/g, "/").replace(/'/g, `'\\''`)}'`;
42731
+ return {
42732
+ ...payload,
42733
+ gitConfig: ["-c", "credential.helper=", "-c", `credential.https://github.com.helper=!${quoted} auth git-credential`]
42734
+ };
42735
+ }
42736
+ async cloneRepository({ repository, baseDirectory }) {
42737
+ const fullName = String(repository || "").trim();
42738
+ const rawBase = String(baseDirectory || "").trim();
42739
+ if (!REPOSITORY_PATTERN.test(fullName)) {
42740
+ return { success: false, code: "invalid_repository", error: "Invalid GitHub repository" };
42741
+ }
42742
+ if (!rawBase || !this.path.isAbsolute(rawBase)) {
42743
+ return { success: false, code: "invalid_destination", error: "Choose an absolute destination directory" };
42744
+ }
42745
+ const base = this.path.resolve(rawBase);
42746
+ try {
42747
+ const stats = await this.fs.promises.stat(base);
42748
+ if (!stats.isDirectory()) throw new Error("Destination is not a directory");
42749
+ await this.fs.promises.access(base, this.fs.constants.W_OK);
42750
+ } catch (error) {
42751
+ return { success: false, code: "invalid_destination", error: this._message(error) };
42752
+ }
42753
+ const name = fullName.split("/")[1];
42754
+ const destination = this.path.resolve(base, name);
42755
+ if (this.path.dirname(destination) !== base) {
42756
+ return { success: false, code: "invalid_destination", error: "Invalid destination directory" };
42757
+ }
42758
+ if (this.fs.existsSync(destination)) {
42759
+ return { success: false, code: "destination_exists", error: "The destination folder already exists", path: destination };
42760
+ }
42761
+ try {
42762
+ await this._run(["repo", "clone", fullName, destination], { timeout: 30 * 6e4 });
42763
+ const stats = await this.fs.promises.stat(destination);
42764
+ if (!stats.isDirectory()) throw new Error("GitHub CLI did not create the repository directory");
42765
+ return { success: true, path: destination, repository: fullName };
42766
+ } catch (error) {
42767
+ return { success: false, code: error.code || "clone_failed", error: this._message(error) };
42768
+ }
42769
+ }
42770
+ };
42771
+ module2.exports = { GitHubProjectImportService, REPOSITORY_PATTERN };
42772
+ }
42773
+ });
42774
+
42353
42775
  // src/infrastructure/headless/headless-task-service.js
42354
42776
  var require_headless_task_service = __commonJS({
42355
42777
  "src/infrastructure/headless/headless-task-service.js"(exports2, module2) {
@@ -45486,7 +45908,7 @@ var require_database = __commonJS({
45486
45908
  { status_key: "needs_testing", label: "Needs testing", color: "#3b82f6", icon: "flask-conical", sort_order: 2, is_default: 1, agent_settable: 1, prompt: "Set it when you finish the implementation and the work is pending the user testing it manually. Do not set it if there are still things left to implement." },
45487
45909
  { status_key: "working", label: "Working", color: "#fbbf24", icon: "hammer", sort_order: 3, is_default: 1, agent_settable: 1, prompt: "Set it when you start working on any request and while you are implementing, investigating or fixing something." },
45488
45910
  { status_key: "done", label: "Done", color: "#22c55e", icon: "circle-check", sort_order: 4, is_default: 1, agent_settable: 1, prompt: "Set it when the work is completely finished: implemented, validated and with its commit/push done when applicable. It is the final state." },
45489
- { status_key: "idle", label: "Idle", color: "#6b7280", icon: "circle-dashed", sort_order: 5, is_default: 1, agent_settable: 0, prompt: "Set by the app when an agent is resting without an explicit work status, including after a reply finishes. It does not mean the work is complete or that user input is required. Agents cannot set this status." }
45911
+ { status_key: "idle", label: "Idle", color: "#6b7280", icon: "circle-dashed", sort_order: 5, is_default: 1, agent_settable: 0, prompt: "Set by the app on an agent that has just been opened and has not been given any work yet. Agents cannot set this status; it clears itself as soon as you send the agent something." }
45490
45912
  ];
45491
45913
  var IDLE_TERMINAL_STATUS_KEY = "idle";
45492
45914
  var IDLE_STATUS_SEEDED_SETTING = "terminal_status_idle_seeded";
@@ -45499,7 +45921,7 @@ var require_database = __commonJS({
45499
45921
  done: 6
45500
45922
  };
45501
45923
  var LEGACY_DEFAULT_PROMPTS = {
45502
- idle: "Set by the app on an agent that has just been opened and has not been given any work yet. Agents cannot set this status; it clears itself as soon as you send the agent something.",
45924
+ idle: "Set by the app when an agent is resting without an explicit work status, including after a reply finishes. It does not mean the work is complete or that user input is required. Agents cannot set this status.",
45503
45925
  working: "Ponlo al empezar a trabajar en cualquier petici\xF3n y mientras est\xE9s implementando, investigando o arreglando algo.",
45504
45926
  needs_input: "Ponlo cuando pares porque necesites una respuesta o decisi\xF3n del usuario para continuar (una pregunta, una elecci\xF3n de dise\xF1o, un permiso).",
45505
45927
  needs_testing: "Ponlo cuando termines la implementaci\xF3n y el trabajo quede pendiente de que el usuario lo pruebe a mano. No lo pongas si a\xFAn quedan cosas por implementar.",
@@ -49548,6 +49970,7 @@ var require_headless_runtime = __commonJS({
49548
49970
  var { HeadlessSessionBridge } = require_headless_session_bridge();
49549
49971
  var { createHeadlessChatPreferences } = require_headless_chat_preferences();
49550
49972
  var { HeadlessProjectRegistry } = require_headless_project_registry();
49973
+ var { GitHubProjectImportService } = require_github_project_import_service();
49551
49974
  var { AGENT_IDS, HeadlessProviderService } = require_headless_provider_service();
49552
49975
  var { HeadlessTaskService } = require_headless_task_service();
49553
49976
  var {
@@ -49582,11 +50005,14 @@ var require_headless_runtime = __commonJS({
49582
50005
  var HEADLESS_PROJECT_CAPABILITIES = Object.freeze([
49583
50006
  "projects.list",
49584
50007
  "project.directories.list",
50008
+ "project.locations.list",
50009
+ "project.locations.add",
49585
50010
  "project.update",
49586
50011
  "project.register",
49587
50012
  "project.clone",
49588
50013
  "project.clone.cancel",
49589
50014
  "project.git.availability",
50015
+ "project.github.repositories",
49590
50016
  "project.unregister",
49591
50017
  "shortcuts.manage",
49592
50018
  "session.action",
@@ -50354,9 +50780,12 @@ ${message}`;
50354
50780
  workspaceGitCreate: inProject(workspace.gitCreate),
50355
50781
  listProjects: (payload) => registry.list(payload),
50356
50782
  listProjectDirectories: (payload) => registry.listDirectories(payload),
50783
+ listProjectLocations: (payload) => registry.listLocations(payload),
50784
+ addProjectLocation: (payload) => registry.addLocation(payload),
50357
50785
  updateProject: (payload) => registry.update(payload),
50358
50786
  registerProject: (payload) => registry.register(payload),
50359
- cloneProject: (payload) => registry.clone(payload),
50787
+ cloneProject: async (payload) => registry.clone(await new GitHubProjectImportService().prepareRemoteClone(payload)),
50788
+ listGitHubRepositories: () => new GitHubProjectImportService().remoteRepositories(),
50360
50789
  cancelProjectClone: (payload) => registry.cancelClone(payload),
50361
50790
  gitAvailability: () => registry.gitAvailability(),
50362
50791
  unregisterProject: (payload) => {
@@ -51043,7 +51472,7 @@ var require_cas = __commonJS({
51043
51472
  loadIdentity,
51044
51473
  resolveProject
51045
51474
  } = require_headless_runtime();
51046
- var version = true ? "0.0.17" : JSON.parse(fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf8")).version;
51475
+ var version = true ? "0.0.18" : JSON.parse(fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf8")).version;
51047
51476
  function help() {
51048
51477
  return `CAS CLI ${version}
51049
51478
 
@@ -1068,7 +1068,7 @@ var require_database_mcp_standalone = __commonJS({
1068
1068
  { status_key: "needs_testing", label: "Needs testing", color: "#3b82f6", icon: "flask-conical", sort_order: 2, is_default: 1, agent_settable: 1, prompt: "Set it when you finish the implementation and the work is pending the user testing it manually. Do not set it if there are still things left to implement." },
1069
1069
  { status_key: "working", label: "Working", color: "#fbbf24", icon: "hammer", sort_order: 3, is_default: 1, agent_settable: 1, prompt: "Set it when you start working on any request and while you are implementing, investigating or fixing something." },
1070
1070
  { status_key: "done", label: "Done", color: "#22c55e", icon: "circle-check", sort_order: 4, is_default: 1, agent_settable: 1, prompt: "Set it when the work is completely finished: implemented, validated and with its commit/push done when applicable. It is the final state." },
1071
- { status_key: "idle", label: "Idle", color: "#6b7280", icon: "circle-dashed", sort_order: 5, is_default: 1, agent_settable: 0, prompt: "Set by the app when an agent is resting without an explicit work status, including after a reply finishes. It does not mean the work is complete or that user input is required. Agents cannot set this status." }
1071
+ { status_key: "idle", label: "Idle", color: "#6b7280", icon: "circle-dashed", sort_order: 5, is_default: 1, agent_settable: 0, prompt: "Set by the app on an agent that has just been opened and has not been given any work yet. Agents cannot set this status; it clears itself as soon as you send the agent something." }
1072
1072
  ];
1073
1073
  var IDLE_TERMINAL_STATUS_KEY = "idle";
1074
1074
  var IDLE_STATUS_SEEDED_SETTING = "terminal_status_idle_seeded";
@@ -1081,7 +1081,7 @@ var require_database_mcp_standalone = __commonJS({
1081
1081
  done: 6
1082
1082
  };
1083
1083
  var LEGACY_DEFAULT_PROMPTS = {
1084
- idle: "Set by the app on an agent that has just been opened and has not been given any work yet. Agents cannot set this status; it clears itself as soon as you send the agent something.",
1084
+ idle: "Set by the app when an agent is resting without an explicit work status, including after a reply finishes. It does not mean the work is complete or that user input is required. Agents cannot set this status.",
1085
1085
  working: "Ponlo al empezar a trabajar en cualquier petici\xF3n y mientras est\xE9s implementando, investigando o arreglando algo.",
1086
1086
  needs_input: "Ponlo cuando pares porque necesites una respuesta o decisi\xF3n del usuario para continuar (una pregunta, una elecci\xF3n de dise\xF1o, un permiso).",
1087
1087
  needs_testing: "Ponlo cuando termines la implementaci\xF3n y el trabajo quede pendiente de que el usuario lo pruebe a mano. No lo pongas si a\xFAn quedan cosas por implementar.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeagentswarm/cas-cloud",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "description": "CAS Cloud runtime for remote CodeAgentSwarm sessions",
5
5
  "author": "Arturo Garcia <hello@codeagentswarm.com>",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",