@c4a/daemon 0.4.12-alpha.3 → 0.4.12-beta.7

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 (3) hide show
  1. package/index.js +783 -62
  2. package/package.json +3 -2
  3. package/scheduler/index.js +14061 -0
package/index.js CHANGED
@@ -8794,6 +8794,7 @@ var RelationType;
8794
8794
  RelationType2["Triggers"] = "TRIGGERS";
8795
8795
  RelationType2["References"] = "REFERENCES";
8796
8796
  RelationType2["Corresponds"] = "CORRESPONDS";
8797
+ RelationType2["Mentions"] = "MENTIONS";
8797
8798
  })(RelationType ||= {});
8798
8799
  var AtomField;
8799
8800
  ((AtomField2) => {
@@ -8811,6 +8812,7 @@ var AtomField;
8811
8812
  AtomField2["Constraints"] = "constraints";
8812
8813
  AtomField2["Comparisons"] = "comparisons";
8813
8814
  AtomField2["Boundaries"] = "boundaries";
8815
+ AtomField2["Claims"] = "claims";
8814
8816
  })(AtomField ||= {});
8815
8817
  var Kind;
8816
8818
  ((Kind2) => {
@@ -8823,6 +8825,161 @@ var Perspective;
8823
8825
  Perspective2["Business"] = "business";
8824
8826
  Perspective2["Technical"] = "technical";
8825
8827
  })(Perspective ||= {});
8828
+ // ../core/src/types/edgeSyncRules.ts
8829
+ var EDGE_SYNC_RULES = [
8830
+ {
8831
+ field: "container_id",
8832
+ sourceEntityType: "component" /* Component */,
8833
+ edgeType: "CONTAINS" /* Contains */,
8834
+ direction: "reverse",
8835
+ writeGraph: true
8836
+ },
8837
+ {
8838
+ field: "system_id",
8839
+ sourceEntityType: "container" /* Container */,
8840
+ edgeType: "CONTAINS" /* Contains */,
8841
+ direction: "reverse",
8842
+ writeGraph: true
8843
+ },
8844
+ {
8845
+ field: "ref_sor",
8846
+ sourceEntityType: "sor" /* SoR */,
8847
+ edgeType: "REFERENCES" /* References */,
8848
+ direction: "forward",
8849
+ writeGraph: true
8850
+ },
8851
+ {
8852
+ field: "product_refs",
8853
+ sourceEntityType: "epic" /* Epic */,
8854
+ edgeType: "REFERENCES" /* References */,
8855
+ direction: "forward",
8856
+ writeGraph: true
8857
+ },
8858
+ {
8859
+ field: "atoms.entities.ref",
8860
+ sourceEntityType: "any",
8861
+ edgeType: "MENTIONS" /* Mentions */,
8862
+ direction: "forward",
8863
+ writeGraph: true
8864
+ },
8865
+ {
8866
+ field: "atoms.behaviors.ref",
8867
+ sourceEntityType: "any",
8868
+ edgeType: "MENTIONS" /* Mentions */,
8869
+ direction: "forward",
8870
+ writeGraph: true
8871
+ },
8872
+ {
8873
+ field: "product_id",
8874
+ sourceEntityType: "sor" /* SoR */,
8875
+ edgeType: null,
8876
+ direction: "forward",
8877
+ writeGraph: false
8878
+ },
8879
+ {
8880
+ field: "process_id",
8881
+ sourceEntityType: "sor" /* SoR */,
8882
+ edgeType: "PRODUCES" /* Produces */,
8883
+ direction: "reverse",
8884
+ writeGraph: true
8885
+ }
8886
+ ];
8887
+ // ../core/src/types/serverConfig.ts
8888
+ var DEFAULT_SERVER_CONFIG = {
8889
+ server: {
8890
+ port: 5100,
8891
+ host: "::"
8892
+ },
8893
+ git_hosts: [],
8894
+ daemon_scheduler: {
8895
+ enabled: true,
8896
+ host: "localhost",
8897
+ port: 5110,
8898
+ workspace: "./data/daemon-workspace",
8899
+ max_daemons: 20
8900
+ },
8901
+ doc_db: {
8902
+ provider: "sqlite",
8903
+ sqlite: {
8904
+ path: "./data/c4a.db"
8905
+ },
8906
+ mongodb: {
8907
+ uri: "mongodb://localhost:27017/c4a?directConnection=true",
8908
+ database: "c4a"
8909
+ }
8910
+ },
8911
+ graph_db: {
8912
+ provider: "neo4j",
8913
+ neo4j: {
8914
+ uri: "bolt://localhost:7687",
8915
+ username: "neo4j",
8916
+ password: ""
8917
+ },
8918
+ nebulagraph: {
8919
+ uri: "localhost:9669",
8920
+ username: "root",
8921
+ password: "nebula",
8922
+ space: "c4a"
8923
+ },
8924
+ duckdb: {
8925
+ path: "./data/c4a-graph.duckdb"
8926
+ }
8927
+ },
8928
+ vector_db: {
8929
+ provider: "lancedb",
8930
+ collection_prefix: "c4a_",
8931
+ qdrant: {
8932
+ url: "http://localhost:6333",
8933
+ api_key: ""
8934
+ },
8935
+ milvus: {
8936
+ address: "localhost:19530"
8937
+ },
8938
+ lancedb: {
8939
+ path: "./data/lancedb"
8940
+ }
8941
+ },
8942
+ llm: {
8943
+ provider: "openai",
8944
+ openai: {
8945
+ api_key: "",
8946
+ base_url: "",
8947
+ default_model: "gpt-5.3-codex"
8948
+ },
8949
+ anthropic: {
8950
+ api_key: "",
8951
+ base_url: "",
8952
+ default_model: "claude-opus-4-6"
8953
+ },
8954
+ google: {
8955
+ api_key: "",
8956
+ base_url: "",
8957
+ default_model: "gemini-3-pro-preview"
8958
+ }
8959
+ },
8960
+ embedding: {
8961
+ provider: "huggingface",
8962
+ huggingface: {
8963
+ model_id: "Xenova/all-MiniLM-L6-v2",
8964
+ dtype: "q8",
8965
+ cache_dir: "./models/embedding"
8966
+ },
8967
+ xenova: {
8968
+ model_id: "Xenova/bge-m3",
8969
+ dtype: "q8",
8970
+ cache_dir: "./models/embedding"
8971
+ },
8972
+ openai: {
8973
+ api_key: "",
8974
+ base_url: "",
8975
+ model_id: "text-embedding-3-small"
8976
+ },
8977
+ google: {
8978
+ api_key: "",
8979
+ model_id: "text-embedding-004"
8980
+ }
8981
+ }
8982
+ };
8826
8983
  // ../core/src/types/daemon.ts
8827
8984
  var CODE_PACKAGE_FILES = new Set([
8828
8985
  "package.json",
@@ -12867,7 +13024,7 @@ var atomFieldSchema = exports_external.enum(Object.values(AtomField));
12867
13024
  var kindSchema = exports_external.enum(Object.values(Kind));
12868
13025
  var scopeSchema = exports_external.literal("project");
12869
13026
  var perspectiveSchema = exports_external.enum(Object.values(Perspective));
12870
- var confidenceSchema = exports_external.object({
13027
+ var extractionConfidenceSchema = exports_external.object({
12871
13028
  structural: exports_external.number().min(0).max(1),
12872
13029
  semantic: exports_external.number().min(0).max(1)
12873
13030
  });
@@ -12875,13 +13032,29 @@ var metadataSchema = exports_external.object({
12875
13032
  created_at: exports_external.string().datetime(),
12876
13033
  updated_at: exports_external.string().datetime(),
12877
13034
  version_tag: exports_external.string().optional(),
12878
- confidence: confidenceSchema.optional()
12879
- });
13035
+ extraction_confidence: extractionConfidenceSchema.optional(),
13036
+ confidence: exports_external.number().min(0).max(1).optional(),
13037
+ aliases: exports_external.array(exports_external.string()).optional(),
13038
+ rank: exports_external.number().optional(),
13039
+ community_id: exports_external.string().optional(),
13040
+ community_level: exports_external.number().optional()
13041
+ });
13042
+ var entityRefPointerSchema = exports_external.string().regex(/^ref:entity:.+$/, "must be a ref pointer in format ref:entity:<id>");
13043
+ var sourceRefSpanSchema = exports_external.object({
13044
+ start: exports_external.number().int().min(1),
13045
+ end: exports_external.number().int().min(1)
13046
+ });
13047
+ var sourceRefSchema = exports_external.object({
13048
+ ref: exports_external.string(),
13049
+ span: sourceRefSpanSchema.optional()
13050
+ });
13051
+ var sourceRefsSchema = exports_external.array(sourceRefSchema).optional();
12880
13052
  // ../core/src/schemas/atomsSchema.ts
12881
13053
  var confidenceAtomSchema = exports_external.number().min(0).max(1).optional();
12882
13054
  var entityAtomSchema = exports_external.object({
12883
13055
  name: exports_external.string(),
12884
13056
  kind: kindSchema.optional(),
13057
+ ref: exports_external.string().optional(),
12885
13058
  confidence: confidenceAtomSchema
12886
13059
  });
12887
13060
  var relationAtomSchema = exports_external.object({
@@ -12895,6 +13068,8 @@ var behaviorAtomSchema = exports_external.object({
12895
13068
  name: exports_external.string(),
12896
13069
  signature: exports_external.string().optional(),
12897
13070
  description: exports_external.string().optional(),
13071
+ performed_by: exports_external.array(exports_external.string()).optional(),
13072
+ ref: exports_external.string().optional(),
12898
13073
  confidence: confidenceAtomSchema
12899
13074
  });
12900
13075
  var attributeAtomSchema = exports_external.object({
@@ -12935,6 +13110,7 @@ var decisionPhaseSchema = exports_external.object({
12935
13110
  rationale: exports_external.string().optional()
12936
13111
  });
12937
13112
  var decisionAtomSchema = exports_external.object({
13113
+ name: exports_external.string().optional(),
12938
13114
  description: exports_external.string(),
12939
13115
  rationale: exports_external.string(),
12940
13116
  alternatives: exports_external.array(exports_external.string()).optional(),
@@ -12957,9 +13133,9 @@ var metricAtomSchema = exports_external.object({
12957
13133
  });
12958
13134
  var roleAtomSchema = exports_external.object({
12959
13135
  name: exports_external.string(),
12960
- kind: exports_external.enum(["internal", "external"]),
13136
+ kind: exports_external.enum(["human", "team", "persona"]),
12961
13137
  performs: exports_external.array(exports_external.string()).optional(),
12962
- confidence: confidenceAtomSchema
13138
+ description: exports_external.string().optional()
12963
13139
  });
12964
13140
  var constraintAtomSchema = exports_external.object({
12965
13141
  description: exports_external.string(),
@@ -12990,6 +13166,16 @@ var comparisonAtomSchema = exports_external.object({
12990
13166
  decision_ref: exports_external.string().optional(),
12991
13167
  confidence: confidenceAtomSchema
12992
13168
  });
13169
+ var claimAtomSchema = exports_external.object({
13170
+ claim_type: exports_external.string(),
13171
+ description: exports_external.string(),
13172
+ status: exports_external.enum(["active", "deprecated", "proposed"]),
13173
+ valid_from: exports_external.string(),
13174
+ valid_until: exports_external.string().optional(),
13175
+ session_ref: exports_external.string().optional(),
13176
+ changed_by: exports_external.string(),
13177
+ confidence: confidenceAtomSchema
13178
+ });
12993
13179
  var dataAtomsSchema = exports_external.object({
12994
13180
  entities: exports_external.array(entityAtomSchema).optional(),
12995
13181
  relations: exports_external.array(relationAtomSchema).optional(),
@@ -13004,31 +13190,115 @@ var dataAtomsSchema = exports_external.object({
13004
13190
  roles: exports_external.array(roleAtomSchema).optional(),
13005
13191
  constraints: exports_external.array(constraintAtomSchema).optional(),
13006
13192
  comparisons: exports_external.array(comparisonAtomSchema).optional(),
13007
- boundaries: exports_external.array(boundaryAtomSchema).optional()
13193
+ boundaries: exports_external.array(boundaryAtomSchema).optional(),
13194
+ claims: exports_external.array(claimAtomSchema).optional()
13008
13195
  }).superRefine((atoms2, ctx) => {
13009
- if (!atoms2.transitions || atoms2.transitions.length === 0) {
13010
- return;
13011
- }
13012
13196
  const stateValues = new Set;
13197
+ const stateNames = new Set;
13013
13198
  for (const state of atoms2.states ?? []) {
13199
+ stateNames.add(state.name);
13014
13200
  for (const value of state.values) {
13015
13201
  stateValues.add(value);
13016
13202
  }
13017
13203
  }
13018
- for (const transition of atoms2.transitions) {
13019
- if (!stateValues.has(transition.from)) {
13020
- ctx.addIssue({
13021
- code: exports_external.ZodIssueCode.custom,
13022
- path: ["transitions"],
13023
- message: `transition.from references unknown state: ${transition.from}`
13024
- });
13204
+ const behaviorNames = new Set;
13205
+ for (const behavior of atoms2.behaviors ?? []) {
13206
+ behaviorNames.add(behavior.name);
13207
+ }
13208
+ const roleNames = new Set;
13209
+ for (const role of atoms2.roles ?? []) {
13210
+ roleNames.add(role.name);
13211
+ }
13212
+ const eventNames = new Set;
13213
+ for (const event of atoms2.events ?? []) {
13214
+ eventNames.add(event.name);
13215
+ }
13216
+ const metricNames = new Set;
13217
+ for (const metric of atoms2.metrics ?? []) {
13218
+ metricNames.add(metric.name);
13219
+ }
13220
+ if (atoms2.transitions && atoms2.transitions.length > 0) {
13221
+ for (const transition of atoms2.transitions) {
13222
+ if (!stateValues.has(transition.from)) {
13223
+ ctx.addIssue({
13224
+ code: exports_external.ZodIssueCode.custom,
13225
+ path: ["transitions"],
13226
+ message: `transition.from references unknown state: ${transition.from}`
13227
+ });
13228
+ }
13229
+ if (!stateValues.has(transition.to)) {
13230
+ ctx.addIssue({
13231
+ code: exports_external.ZodIssueCode.custom,
13232
+ path: ["transitions"],
13233
+ message: `transition.to references unknown state: ${transition.to}`
13234
+ });
13235
+ }
13236
+ const hasEvents = eventNames.size > 0;
13237
+ const hasBehaviors = behaviorNames.size > 0;
13238
+ if (hasEvents || hasBehaviors) {
13239
+ const triggerMatchesEvent = hasEvents && eventNames.has(transition.trigger);
13240
+ const triggerMatchesBehavior = hasBehaviors && behaviorNames.has(transition.trigger);
13241
+ if (!triggerMatchesEvent && !triggerMatchesBehavior) {
13242
+ ctx.addIssue({
13243
+ code: exports_external.ZodIssueCode.custom,
13244
+ path: ["transitions"],
13245
+ message: `transition.trigger references unknown event/behavior: ${transition.trigger}`
13246
+ });
13247
+ }
13248
+ }
13025
13249
  }
13026
- if (!stateValues.has(transition.to)) {
13027
- ctx.addIssue({
13028
- code: exports_external.ZodIssueCode.custom,
13029
- path: ["transitions"],
13030
- message: `transition.to references unknown state: ${transition.to}`
13031
- });
13250
+ }
13251
+ if (atoms2.roles && atoms2.roles.length > 0) {
13252
+ for (const [index, role] of atoms2.roles.entries()) {
13253
+ for (const perform of role.performs ?? []) {
13254
+ if (!behaviorNames.has(perform)) {
13255
+ ctx.addIssue({
13256
+ code: exports_external.ZodIssueCode.custom,
13257
+ path: ["roles", index, "performs"],
13258
+ message: `role.performs references unknown behavior: ${perform}`
13259
+ });
13260
+ }
13261
+ }
13262
+ }
13263
+ }
13264
+ if (atoms2.behaviors && atoms2.behaviors.length > 0) {
13265
+ for (const [index, behavior] of atoms2.behaviors.entries()) {
13266
+ for (const performer of behavior.performed_by ?? []) {
13267
+ if (!roleNames.has(performer)) {
13268
+ ctx.addIssue({
13269
+ code: exports_external.ZodIssueCode.custom,
13270
+ path: ["behaviors", index, "performed_by"],
13271
+ message: `behavior.performed_by references unknown role: ${performer}`
13272
+ });
13273
+ }
13274
+ }
13275
+ }
13276
+ }
13277
+ if (atoms2.rules && atoms2.rules.length > 0) {
13278
+ const allowedDependsOn = new Set([...stateNames, ...metricNames]);
13279
+ for (const [index, rule] of atoms2.rules.entries()) {
13280
+ for (const dependency of rule.depends_on ?? []) {
13281
+ if (!allowedDependsOn.has(dependency)) {
13282
+ ctx.addIssue({
13283
+ code: exports_external.ZodIssueCode.custom,
13284
+ path: ["rules", index, "depends_on"],
13285
+ message: `rule.depends_on references unknown state/metric: ${dependency}`
13286
+ });
13287
+ }
13288
+ }
13289
+ }
13290
+ }
13291
+ if (atoms2.events && atoms2.events.length > 0) {
13292
+ for (const [index, event] of atoms2.events.entries()) {
13293
+ for (const behaviorName of event.trigger_for ?? []) {
13294
+ if (!behaviorNames.has(behaviorName)) {
13295
+ ctx.addIssue({
13296
+ code: exports_external.ZodIssueCode.custom,
13297
+ path: ["events", index, "trigger_for"],
13298
+ message: `event.trigger_for references unknown behavior: ${behaviorName}`
13299
+ });
13300
+ }
13301
+ }
13032
13302
  }
13033
13303
  }
13034
13304
  });
@@ -13044,7 +13314,8 @@ var atomsWhitelistByEntityType = {
13044
13314
  "states" /* States */,
13045
13315
  "rules" /* Rules */,
13046
13316
  "transitions" /* Transitions */,
13047
- "events" /* Events */
13317
+ "events" /* Events */,
13318
+ "claims" /* Claims */
13048
13319
  ]),
13049
13320
  ["process" /* Process */]: new Set,
13050
13321
  ["sor" /* SoR */]: new Set([
@@ -13060,7 +13331,8 @@ var atomsWhitelistByEntityType = {
13060
13331
  "metrics" /* Metrics */,
13061
13332
  "roles" /* Roles */,
13062
13333
  "constraints" /* Constraints */,
13063
- "comparisons" /* Comparisons */
13334
+ "comparisons" /* Comparisons */,
13335
+ "claims" /* Claims */
13064
13336
  ]),
13065
13337
  ["contract" /* Contract */]: new Set,
13066
13338
  ["epic" /* Epic */]: new Set([
@@ -13068,7 +13340,8 @@ var atomsWhitelistByEntityType = {
13068
13340
  "metrics" /* Metrics */,
13069
13341
  "roles" /* Roles */,
13070
13342
  "constraints" /* Constraints */,
13071
- "comparisons" /* Comparisons */
13343
+ "comparisons" /* Comparisons */,
13344
+ "claims" /* Claims */
13072
13345
  ]),
13073
13346
  ["document" /* Document */]: new Set
13074
13347
  };
@@ -13093,7 +13366,8 @@ var baseEntitySchema = exports_external.object({
13093
13366
  kind: kindSchema,
13094
13367
  scope: scopeSchema,
13095
13368
  perspective: perspectiveSchema,
13096
- metadata: metadataSchema
13369
+ metadata: metadataSchema,
13370
+ source_refs: sourceRefsSchema
13097
13371
  });
13098
13372
  var productDataSchema = exports_external.object({
13099
13373
  description: exports_external.string().optional(),
@@ -13107,7 +13381,8 @@ var productEntitySchema = baseEntitySchema.extend({
13107
13381
  });
13108
13382
  var systemDataSchema = exports_external.object({
13109
13383
  description: exports_external.string().optional(),
13110
- corresponds_to: exports_external.string().optional()
13384
+ corresponds_to: exports_external.string().optional(),
13385
+ atoms: exports_external.undefined().optional()
13111
13386
  });
13112
13387
  var systemEntitySchema = baseEntitySchema.extend({
13113
13388
  type: exports_external.literal("system" /* System */),
@@ -13153,9 +13428,10 @@ var sorEntitySchema = baseEntitySchema.extend({
13153
13428
  data: sorDataSchema
13154
13429
  });
13155
13430
  var contractDataSchema = exports_external.object({
13156
- format: exports_external.enum(["openapi", "asyncapi", "proto"]),
13431
+ format: exports_external.enum(["openapi", "asyncapi", "proto", "graphql", "custom"]),
13157
13432
  spec: exports_external.string(),
13158
- component_id: exports_external.string().optional()
13433
+ version: exports_external.string().optional(),
13434
+ description: exports_external.string().optional()
13159
13435
  });
13160
13436
  var contractEntitySchema = baseEntitySchema.extend({
13161
13437
  type: exports_external.literal("contract" /* Contract */),
@@ -13163,7 +13439,7 @@ var contractEntitySchema = baseEntitySchema.extend({
13163
13439
  });
13164
13440
  var epicDataSchema = exports_external.object({
13165
13441
  description: exports_external.string().optional(),
13166
- product_refs: exports_external.array(exports_external.string()).optional(),
13442
+ product_refs: exports_external.array(entityRefPointerSchema).optional(),
13167
13443
  atoms: dataAtomsSchema.optional()
13168
13444
  });
13169
13445
  var epicEntitySchema = baseEntitySchema.extend({
@@ -13239,18 +13515,45 @@ var entitySchema = exports_external.discriminatedUnion("type", [
13239
13515
  });
13240
13516
  }
13241
13517
  }
13518
+ if (entity2.type === "sor" /* SoR */ && entity2.data.atoms) {
13519
+ const { constraints, rules } = entity2.data.atoms;
13520
+ const hasConstraints = Array.isArray(constraints) && constraints.length > 0;
13521
+ const hasRules = Array.isArray(rules) && rules.length > 0;
13522
+ if (!hasConstraints && !hasRules) {
13523
+ ctx.addIssue({
13524
+ code: exports_external.ZodIssueCode.custom,
13525
+ path: ["data", "atoms"],
13526
+ message: "SoR must have at least one constraint or rule"
13527
+ });
13528
+ }
13529
+ }
13530
+ if (entity2.type === "epic" /* Epic */ && entity2.data.atoms) {
13531
+ const { decisions } = entity2.data.atoms;
13532
+ const hasDecisions = Array.isArray(decisions) && decisions.length > 0;
13533
+ if (!hasDecisions) {
13534
+ ctx.addIssue({
13535
+ code: exports_external.ZodIssueCode.custom,
13536
+ path: ["data", "atoms"],
13537
+ message: "Epic must have at least one decision"
13538
+ });
13539
+ }
13540
+ }
13242
13541
  });
13243
13542
  // ../core/src/schemas/relationSchema.ts
13244
13543
  var relationDataSchema = exports_external.object({
13245
- description: exports_external.string().optional()
13544
+ description: exports_external.string().optional(),
13545
+ _source: exports_external.literal("field_sync").optional()
13246
13546
  });
13247
13547
  var relationSchema = exports_external.object({
13248
13548
  id: exports_external.string(),
13249
13549
  type: relationTypeSchema,
13250
13550
  from: exports_external.string(),
13251
13551
  to: exports_external.string(),
13552
+ weight: exports_external.number().optional(),
13553
+ confidence: exports_external.number().min(0).max(1).optional(),
13252
13554
  data: relationDataSchema.optional(),
13253
- metadata: metadataSchema.optional()
13555
+ metadata: metadataSchema.optional(),
13556
+ source_refs: sourceRefsSchema
13254
13557
  });
13255
13558
  // ../core/src/errors/httpStatus.ts
13256
13559
  var ERROR_CODE_HTTP_STATUS = {
@@ -13288,6 +13591,7 @@ var ERROR_CODE_HTTP_STATUS = {
13288
13591
  var import_yaml = __toESM(require_dist(), 1);
13289
13592
  // ../core/src/constants.ts
13290
13593
  var DAEMON_HEARTBEAT_INTERVAL = 30000;
13594
+ var CLOUD_DAEMON_IDLE_TIMEOUT = 300000;
13291
13595
  // ../core/src/fileTypes.ts
13292
13596
  var INDEXABLE_CODE_EXTENSIONS = new Set([".ts", ".tsx"]);
13293
13597
  var INDEXABLE_DOC_EXTENSIONS = new Set([".md"]);
@@ -13385,18 +13689,22 @@ function resolveServerUrl(input) {
13385
13689
  }
13386
13690
  const isLocal = host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("0.0.0.0");
13387
13691
  const protocol = isLocal ? "ws" : "wss";
13388
- const base2 = host.replace(/^localhost/, "127.0.0.1").replace(/\/+$/, "");
13692
+ const base2 = host.replace(/\/+$/, "");
13389
13693
  return `${protocol}://${base2}${WS_DAEMON_PATH}`;
13390
13694
  }
13391
- var parseAllowedPaths = (input) => {
13695
+ var parseAllowedPaths = (input, extraPath) => {
13392
13696
  if (!input) {
13393
- return ["*"];
13697
+ return extraPath ? ["*", extraPath] : ["*"];
13394
13698
  }
13395
13699
  const parts = input.split(",").map((s) => s.trim()).filter(Boolean);
13396
13700
  if (parts.includes("*")) {
13397
- return ["*"];
13701
+ return extraPath ? ["*", extraPath] : ["*"];
13398
13702
  }
13399
- return normalizeAllowedPaths(parts);
13703
+ const normalized = normalizeAllowedPaths(parts);
13704
+ if (extraPath && !normalized.includes("*")) {
13705
+ normalized.push(extraPath);
13706
+ }
13707
+ return normalizeAllowedPaths(normalized);
13400
13708
  };
13401
13709
 
13402
13710
  // src/handshake.ts
@@ -13429,8 +13737,8 @@ var readPackageVersion = (packageRoot = getPackageRoot()) => {
13429
13737
  const parsed = JSON.parse(raw);
13430
13738
  return parsed.version ?? "0.0.0";
13431
13739
  };
13432
- var buildHandshakePayload = (allowedPaths) => {
13433
- const machineId = os.hostname();
13740
+ var buildHandshakePayload = (allowedPaths, machineIdOverride) => {
13741
+ const machineId = machineIdOverride?.trim() || os.hostname();
13434
13742
  return {
13435
13743
  machine_id: machineId,
13436
13744
  machine_name: machineId,
@@ -13713,12 +14021,41 @@ var createFsReadFileHandler = (allowedPaths) => async (params) => {
13713
14021
  // src/handlers/fsRepoStatus.ts
13714
14022
  var REPO_STATUS_CACHE_TTL_MS = 1e4;
13715
14023
  var repoStatusCache = new Map;
14024
+ var parseForEachRef = (raw) => {
14025
+ const entries = [];
14026
+ for (const line of raw.split(`
14027
+ `)) {
14028
+ if (!line.trim())
14029
+ continue;
14030
+ const [name, commit] = line.split(" ");
14031
+ if (name && commit)
14032
+ entries.push({ name, commit });
14033
+ }
14034
+ return entries;
14035
+ };
14036
+ var parseLogOutput = (raw) => {
14037
+ const entries = [];
14038
+ for (const line of raw.split(`
14039
+ `)) {
14040
+ if (!line.trim())
14041
+ continue;
14042
+ const spaceIdx = line.indexOf(" ");
14043
+ if (spaceIdx === -1) {
14044
+ entries.push({ hash: line.trim(), message: "" });
14045
+ } else {
14046
+ entries.push({ hash: line.slice(0, spaceIdx), message: line.slice(spaceIdx + 1) });
14047
+ }
14048
+ }
14049
+ return entries;
14050
+ };
13716
14051
  var createFsRepoStatusHandler = (allowedPaths) => async (params) => {
13717
14052
  const now = Date.now();
13718
14053
  const payload = params;
13719
14054
  const rawPath = ensureString(payload?.path, "path");
14055
+ const requestedBranch = typeof payload?.branch === "string" ? payload.branch : "";
13720
14056
  const targetPath = assertAllowedPath(rawPath, allowedPaths);
13721
- const cached = repoStatusCache.get(targetPath);
14057
+ const cacheKey = `${targetPath}:${requestedBranch}`;
14058
+ const cached = repoStatusCache.get(cacheKey);
13722
14059
  if (cached?.value && cached.expiresAt > now) {
13723
14060
  return cached.value;
13724
14061
  }
@@ -13726,26 +14063,36 @@ var createFsRepoStatusHandler = (allowedPaths) => async (params) => {
13726
14063
  return cached.inflight;
13727
14064
  }
13728
14065
  const inflight = (async () => {
13729
- const [branch, headHash, remoteResult] = await Promise.all([
14066
+ const [branch, headHash, remoteResult, branchesRaw, tagsRaw] = await Promise.all([
13730
14067
  runGit(["rev-parse", "--abbrev-ref", "HEAD"], targetPath),
13731
14068
  runGit(["rev-parse", "HEAD"], targetPath),
13732
- tryRunGit(["config", "--get", "remote.origin.url"], targetPath)
14069
+ tryRunGit(["config", "--get", "remote.origin.url"], targetPath),
14070
+ runGit(["for-each-ref", "refs/heads", "--format=%(refname:short) %(objectname)"], targetPath),
14071
+ runGit(["for-each-ref", "refs/tags", "--format=%(refname:short) %(objectname)"], targetPath)
13733
14072
  ]);
13734
14073
  const dirtyResult = await tryRunGit(["diff-index", "--quiet", "HEAD", "--"], targetPath);
14074
+ const branches = parseForEachRef(branchesRaw);
14075
+ const tags = parseForEachRef(tagsRaw);
14076
+ const logBranch = requestedBranch || branch;
14077
+ const logResult = await tryRunGit(["log", logBranch, "--format=%H %s", "-n", "30"], targetPath);
14078
+ const commits = logResult.ok ? parseLogOutput(logResult.stdout) : [];
13735
14079
  return {
13736
14080
  branch,
13737
14081
  head_hash: headHash,
13738
14082
  is_dirty: !dirtyResult.ok,
13739
- remote_url: remoteResult.ok ? remoteResult.stdout.trim() || undefined : undefined
14083
+ remote_url: remoteResult.ok ? remoteResult.stdout.trim() || undefined : undefined,
14084
+ branches,
14085
+ tags,
14086
+ commits
13740
14087
  };
13741
14088
  })();
13742
- repoStatusCache.set(targetPath, { expiresAt: now + REPO_STATUS_CACHE_TTL_MS, inflight });
14089
+ repoStatusCache.set(cacheKey, { expiresAt: now + REPO_STATUS_CACHE_TTL_MS, inflight });
13743
14090
  try {
13744
14091
  const value = await inflight;
13745
- repoStatusCache.set(targetPath, { expiresAt: Date.now() + REPO_STATUS_CACHE_TTL_MS, value });
14092
+ repoStatusCache.set(cacheKey, { expiresAt: Date.now() + REPO_STATUS_CACHE_TTL_MS, value });
13746
14093
  return value;
13747
14094
  } finally {
13748
- const entry = repoStatusCache.get(targetPath);
14095
+ const entry = repoStatusCache.get(cacheKey);
13749
14096
  if (entry?.inflight) {
13750
14097
  delete entry.inflight;
13751
14098
  }
@@ -13753,8 +14100,6 @@ var createFsRepoStatusHandler = (allowedPaths) => async (params) => {
13753
14100
  };
13754
14101
 
13755
14102
  // src/handlers/fsRepoScan.ts
13756
- import { readFile as readFile4 } from "node:fs/promises";
13757
- import path7 from "node:path";
13758
14103
  var DEFAULT_INCLUDE2 = ["manifests", "docs"];
13759
14104
  var matchesFilter2 = (filePath, include) => {
13760
14105
  if (include.includes("*"))
@@ -13816,8 +14161,7 @@ var createFsRepoScanHandler = (allowedPaths) => async (params) => {
13816
14161
  if (!fileName || !CODE_PACKAGE_FILES.has(fileName.toLowerCase()))
13817
14162
  continue;
13818
14163
  try {
13819
- const fullPath = path7.join(targetPath, file.path);
13820
- const content = await readFile4(fullPath, "utf-8");
14164
+ const content = await runGit(["show", `${hash2}:${file.path}`], targetPath);
13821
14165
  if (!isValidManifest(file.path, content))
13822
14166
  continue;
13823
14167
  manifests.push({ path: file.path, content });
@@ -13858,8 +14202,362 @@ var createFsRepoTreeHandler = (allowedPaths) => async (params) => {
13858
14202
  return { files };
13859
14203
  };
13860
14204
 
14205
+ // src/handlers/configSetAuth.ts
14206
+ import { chmod, mkdir, writeFile } from "node:fs/promises";
14207
+
14208
+ // src/handlers/vcsUtils.ts
14209
+ import { execFile as execFile2, spawn } from "node:child_process";
14210
+ import path7 from "node:path";
14211
+ var stripAuthFromUrl = (rawUrl) => {
14212
+ try {
14213
+ const parsed = new URL(rawUrl);
14214
+ if (parsed.username || parsed.password) {
14215
+ parsed.username = "";
14216
+ parsed.password = "";
14217
+ }
14218
+ return `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
14219
+ } catch {
14220
+ return rawUrl;
14221
+ }
14222
+ };
14223
+ var gitConfigPath = (workDir) => path7.join(workDir, ".git-daemon.config");
14224
+ var gitCredentialsPath = (workDir) => path7.join(workDir, ".git-daemon.credentials");
14225
+ var createGitEnv = (workDir) => {
14226
+ const base2 = {
14227
+ ...process.env,
14228
+ LANG: "C",
14229
+ LC_ALL: "C",
14230
+ GIT_TERMINAL_PROMPT: "0"
14231
+ };
14232
+ if (!workDir)
14233
+ return base2;
14234
+ return { ...base2, GIT_CONFIG_GLOBAL: gitConfigPath(workDir) };
14235
+ };
14236
+ var parseGitVersion = (raw) => {
14237
+ const match = raw.match(/git version (\d+)\.(\d+)\.(\d+)/i);
14238
+ if (!match)
14239
+ return null;
14240
+ return {
14241
+ major: Number(match[1]),
14242
+ minor: Number(match[2]),
14243
+ patch: Number(match[3])
14244
+ };
14245
+ };
14246
+ var isGitVersionAtLeast = (version, target) => {
14247
+ if (version.major !== target.major)
14248
+ return version.major > target.major;
14249
+ if (version.minor !== target.minor)
14250
+ return version.minor > target.minor;
14251
+ return version.patch >= target.patch;
14252
+ };
14253
+ var getGitVersion = async (env) => new Promise((resolve) => {
14254
+ execFile2("git", ["--version"], { env }, (error, stdout) => {
14255
+ if (error) {
14256
+ console.log(`[getGitVersion] execFile error: ${error.message}`);
14257
+ return resolve(null);
14258
+ }
14259
+ const raw = stdout.trim();
14260
+ const parsed = parseGitVersion(raw);
14261
+ console.log(`[getGitVersion] raw="${raw}" parsed=${JSON.stringify(parsed)}`);
14262
+ resolve(parsed);
14263
+ });
14264
+ });
14265
+ var EXEC_GIT_MAX_BUFFER = 20 * 1024 * 1024;
14266
+ var execGit = async (args, options = {}) => new Promise((resolve, reject) => {
14267
+ execFile2("git", args, { cwd: options.cwd, env: options.env, maxBuffer: EXEC_GIT_MAX_BUFFER }, (error, stdout, stderr) => {
14268
+ if (error) {
14269
+ reject(error);
14270
+ return;
14271
+ }
14272
+ resolve({ stdout: stdout.toString(), stderr: stderr.toString() });
14273
+ });
14274
+ });
14275
+ var GIT_PROGRESS_RE = /(Enumerating|Receiving|Counting|Compressing|Resolving) (?:objects|deltas):\s+(\d+)%\s*(?:\([^)]*\))?/;
14276
+ var GIT_PHASE_WEIGHTS = {
14277
+ Enumerating: [0, 3],
14278
+ Counting: [3, 6],
14279
+ Compressing: [6, 10],
14280
+ Receiving: [10, 90],
14281
+ Resolving: [90, 100]
14282
+ };
14283
+ var weightedPercent = (phase, rawPercent) => {
14284
+ const [start, end] = GIT_PHASE_WEIGHTS[phase] ?? [0, 100];
14285
+ return Math.round(start + rawPercent / 100 * (end - start));
14286
+ };
14287
+ var runGitWithProgress = async (args, options) => new Promise((resolve, reject) => {
14288
+ console.log(`[runGitWithProgress] spawn: git ${args.join(" ")}`);
14289
+ const child = spawn("git", args, {
14290
+ cwd: options.cwd,
14291
+ env: options.env,
14292
+ stdio: ["ignore", "pipe", "pipe"]
14293
+ });
14294
+ let lastWeighted = null;
14295
+ let stderrBuf = "";
14296
+ child.stderr.on("data", (chunk) => {
14297
+ const text = chunk.toString();
14298
+ stderrBuf += text;
14299
+ const lines = stderrBuf.split(/[\r\n]+/);
14300
+ stderrBuf = lines.pop() ?? "";
14301
+ for (const line of lines) {
14302
+ const trimmed = line.trim();
14303
+ if (!trimmed)
14304
+ continue;
14305
+ options.onStderr?.(trimmed);
14306
+ const match = trimmed.match(GIT_PROGRESS_RE);
14307
+ if (match) {
14308
+ const phase = match[1];
14309
+ const raw = Number(match[2]);
14310
+ if (!Number.isNaN(raw)) {
14311
+ const weighted = weightedPercent(phase, raw);
14312
+ if (weighted !== lastWeighted) {
14313
+ lastWeighted = weighted;
14314
+ options.onProgress?.({ percent: weighted, label: trimmed });
14315
+ }
14316
+ }
14317
+ }
14318
+ }
14319
+ });
14320
+ child.on("error", (error) => {
14321
+ reject(error);
14322
+ });
14323
+ child.on("close", (code) => {
14324
+ if (stderrBuf.trim()) {
14325
+ options.onStderr?.(stderrBuf.trim());
14326
+ }
14327
+ if (code === 0) {
14328
+ console.log("[runGitWithProgress] finished successfully");
14329
+ resolve();
14330
+ } else {
14331
+ reject(new Error(`git ${args.join(" ")} failed with code ${code}`));
14332
+ }
14333
+ });
14334
+ });
14335
+
14336
+ // src/handlers/configSetAuth.ts
14337
+ var ensureWorkDir = (workDir) => {
14338
+ if (!workDir) {
14339
+ throw new RpcError(JSON_RPC_ERROR_CODES.INVALID_PARAMS, "Work dir not configured");
14340
+ }
14341
+ return workDir;
14342
+ };
14343
+ var formatCredentialLines = (hosts) => hosts.map((host) => {
14344
+ const authUser = host.auth_user ?? "oauth2";
14345
+ return `https://${authUser}:${host.token}@${host.host}`;
14346
+ });
14347
+ var createConfigSetAuthHandler = (workDir) => async (params, _context) => {
14348
+ const payload = params;
14349
+ const hosts = Array.isArray(payload?.hosts) ? payload.hosts : null;
14350
+ if (!hosts) {
14351
+ throw new RpcError(JSON_RPC_ERROR_CODES.INVALID_PARAMS, "Invalid hosts");
14352
+ }
14353
+ for (const entry of hosts) {
14354
+ ensureString(entry?.host, "host");
14355
+ ensureString(entry?.auth_user, "auth_user");
14356
+ ensureString(entry?.token, "token");
14357
+ }
14358
+ const rootDir = ensureWorkDir(workDir);
14359
+ await mkdir(rootDir, { recursive: true });
14360
+ const configPath = gitConfigPath(rootDir);
14361
+ const credentialsPath = gitCredentialsPath(rootDir);
14362
+ const configContent = `[credential]
14363
+ helper = store --file ${credentialsPath}
14364
+ `;
14365
+ await writeFile(configPath, configContent, "utf-8");
14366
+ const lines = formatCredentialLines(hosts);
14367
+ await writeFile(credentialsPath, `${lines.join(`
14368
+ `)}
14369
+ `, "utf-8");
14370
+ await chmod(credentialsPath, 384);
14371
+ return { ok: true };
14372
+ };
14373
+
14374
+ // src/handlers/vcsClone.ts
14375
+ import { mkdir as mkdir2, rename, rm } from "node:fs/promises";
14376
+ import path8 from "node:path";
14377
+ var MIN_GIT_VERSION = { major: 2, minor: 5, patch: 0 };
14378
+ var ensureWorkDir2 = (workDir) => {
14379
+ if (!workDir) {
14380
+ throw new RpcError(JSON_RPC_ERROR_CODES.INVALID_PARAMS, "Work dir not configured");
14381
+ }
14382
+ return workDir;
14383
+ };
14384
+ var extractRepoNameFromUrl = (url) => {
14385
+ try {
14386
+ const parsed = new URL(url);
14387
+ const name = parsed.pathname.split("/").filter(Boolean).pop() ?? "repo";
14388
+ return name.replace(/\.git$/i, "") || "repo";
14389
+ } catch {
14390
+ return "repo";
14391
+ }
14392
+ };
14393
+ var getRepoDir = (workDir, repoName) => path8.join(workDir, "repos", repoName);
14394
+ var notifyProgress = (notify, percent, detail) => {
14395
+ if (!notify)
14396
+ return;
14397
+ const payload = {
14398
+ stage: "cloning",
14399
+ label: "cloning",
14400
+ percent,
14401
+ ...detail ? { detail } : {}
14402
+ };
14403
+ notify("task/progress", payload);
14404
+ };
14405
+ var getRemoteUrl = async (repoDir, env) => {
14406
+ try {
14407
+ const result = await execGit(["-C", repoDir, "config", "--get", "remote.origin.url"], { env });
14408
+ const url = result.stdout.trim();
14409
+ return url.length > 0 ? url : null;
14410
+ } catch {
14411
+ return null;
14412
+ }
14413
+ };
14414
+ var getHeadCommit = async (repoDir, env) => {
14415
+ const result = await execGit(["-C", repoDir, "rev-parse", "HEAD"], { env });
14416
+ return result.stdout.trim();
14417
+ };
14418
+ var createVcsCloneHandler = (workDir) => async (params, context) => {
14419
+ const payload = params;
14420
+ const url = ensureString(payload?.url, "url");
14421
+ const ref = payload?.ref;
14422
+ const commit = payload?.commit;
14423
+ if (ref !== undefined) {
14424
+ ensureString(ref, "ref");
14425
+ }
14426
+ if (commit !== undefined) {
14427
+ ensureString(commit, "commit");
14428
+ }
14429
+ const rootDir = ensureWorkDir2(workDir);
14430
+ const repoName = payload?.repo_name || extractRepoNameFromUrl(url);
14431
+ const repoDir = getRepoDir(rootDir, repoName);
14432
+ const env = createGitEnv(rootDir);
14433
+ if (commit) {
14434
+ const version = await getGitVersion(env);
14435
+ console.log(`[vcs/clone] git version check: parsed=${JSON.stringify(version)}, env.PATH=${env?.PATH ?? process.env.PATH}`);
14436
+ if (!version || !isGitVersionAtLeast(version, MIN_GIT_VERSION)) {
14437
+ const current = version ? `${version.major}.${version.minor}.${version.patch}` : "unknown";
14438
+ throw new RpcError(JSON_RPC_ERROR_CODES.INVALID_PARAMS, `Commit hash checkout requires Git 2.5+, current version: ${current}. Please use branch/tag selection instead.`);
14439
+ }
14440
+ }
14441
+ await mkdir2(rootDir, { recursive: true });
14442
+ const legacyRepoDir = path8.join(rootDir, "repo");
14443
+ const hasNewDir = await execGit(["-C", repoDir, "rev-parse", "--git-dir"], { env }).then(() => true).catch(() => false);
14444
+ if (!hasNewDir) {
14445
+ const hasLegacy = await execGit(["-C", legacyRepoDir, "rev-parse", "--git-dir"], { env }).then(() => true).catch(() => false);
14446
+ if (hasLegacy) {
14447
+ const legacyRemote = await getRemoteUrl(legacyRepoDir, env);
14448
+ if (legacyRemote && stripAuthFromUrl(legacyRemote) === stripAuthFromUrl(url)) {
14449
+ await mkdir2(path8.dirname(repoDir), { recursive: true });
14450
+ await rename(legacyRepoDir, repoDir);
14451
+ }
14452
+ }
14453
+ }
14454
+ const hasGitDir = await execGit(["-C", repoDir, "rev-parse", "--git-dir"], { env }).then(() => true).catch(() => false);
14455
+ if (hasGitDir) {
14456
+ const remoteUrl = await getRemoteUrl(repoDir, env);
14457
+ if (remoteUrl && stripAuthFromUrl(remoteUrl) === stripAuthFromUrl(url)) {
14458
+ if (commit) {
14459
+ await runGitWithProgress(["-C", repoDir, "fetch", "--depth=1", "--progress", "origin", commit], {
14460
+ env,
14461
+ onProgress: ({ percent, label }) => notifyProgress(context?.notify, percent, label),
14462
+ onStderr: (text) => console.log(`[vcs/clone] git: ${text}`)
14463
+ });
14464
+ await execGit(["-C", repoDir, "checkout", commit], { env });
14465
+ } else {
14466
+ const refToUse = ref ?? "HEAD";
14467
+ await runGitWithProgress(["-C", repoDir, "fetch", "--depth=1", "--progress", "origin", refToUse], {
14468
+ env,
14469
+ onProgress: ({ percent, label }) => notifyProgress(context?.notify, percent, label),
14470
+ onStderr: (text) => console.log(`[vcs/clone] git: ${text}`)
14471
+ });
14472
+ await execGit(["-C", repoDir, "checkout", "FETCH_HEAD"], { env });
14473
+ }
14474
+ } else {
14475
+ await rm(repoDir, { recursive: true, force: true });
14476
+ }
14477
+ }
14478
+ const repoExists = await execGit(["-C", repoDir, "rev-parse", "--git-dir"], { env }).then(() => true).catch(() => false);
14479
+ if (!repoExists) {
14480
+ if (commit) {
14481
+ await mkdir2(repoDir, { recursive: true });
14482
+ await execGit(["-C", repoDir, "init"], { env });
14483
+ await execGit(["-C", repoDir, "remote", "add", "origin", url], { env });
14484
+ await runGitWithProgress(["-C", repoDir, "fetch", "--depth=1", "--progress", "origin", commit], {
14485
+ env,
14486
+ onProgress: ({ percent, label }) => notifyProgress(context?.notify, percent, label),
14487
+ onStderr: (text) => console.log(`[vcs/clone] git: ${text}`)
14488
+ });
14489
+ await execGit(["-C", repoDir, "checkout", commit], { env });
14490
+ } else if (ref) {
14491
+ await runGitWithProgress(["clone", "--depth=1", "--branch", ref, "--progress", url, repoDir], {
14492
+ env,
14493
+ onProgress: ({ percent, label }) => notifyProgress(context?.notify, percent, label),
14494
+ onStderr: (text) => console.log(`[vcs/clone] git: ${text}`)
14495
+ });
14496
+ } else {
14497
+ await runGitWithProgress(["clone", "--depth=1", "--progress", url, repoDir], {
14498
+ env,
14499
+ onProgress: ({ percent, label }) => notifyProgress(context?.notify, percent, label),
14500
+ onStderr: (text) => console.log(`[vcs/clone] git: ${text}`)
14501
+ });
14502
+ }
14503
+ }
14504
+ const commitHash = await getHeadCommit(repoDir, env);
14505
+ return { path: repoDir, commit_hash: commitHash };
14506
+ };
14507
+
14508
+ // src/handlers/vcsRefs.ts
14509
+ var parseLsRemoteOutput = (raw) => {
14510
+ const entries = [];
14511
+ const lines = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
14512
+ for (const line of lines) {
14513
+ if (line.startsWith("ref: "))
14514
+ continue;
14515
+ const [commit, ref] = line.split(/\s+/);
14516
+ if (!commit || !ref)
14517
+ continue;
14518
+ if (ref.endsWith("^{}"))
14519
+ continue;
14520
+ entries.push({ name: ref, commit });
14521
+ }
14522
+ return entries;
14523
+ };
14524
+ var extractDefaultBranch = (raw) => {
14525
+ const line = raw.split(/\r?\n/).find((item) => item.startsWith("ref: "));
14526
+ if (!line)
14527
+ return null;
14528
+ const match = line.match(/^ref: refs\/heads\/(.+?)\s+HEAD/);
14529
+ return match?.[1] ?? null;
14530
+ };
14531
+ var splitRefs = (entries) => {
14532
+ const branches = [];
14533
+ const tags = [];
14534
+ for (const entry of entries) {
14535
+ if (entry.name.startsWith("refs/heads/")) {
14536
+ branches.push({ name: entry.name.replace("refs/heads/", ""), commit: entry.commit });
14537
+ } else if (entry.name.startsWith("refs/tags/")) {
14538
+ tags.push({ name: entry.name.replace("refs/tags/", ""), commit: entry.commit });
14539
+ }
14540
+ }
14541
+ return { branches, tags };
14542
+ };
14543
+ var createVcsRefsHandler = (workDir) => async (params, _context) => {
14544
+ const payload = params;
14545
+ const url = ensureString(payload?.url, "url");
14546
+ const env = createGitEnv(workDir);
14547
+ try {
14548
+ const out = await execGit(["ls-remote", "--symref", url, "HEAD", "refs/heads/*", "refs/tags/*"], { env });
14549
+ const defaultBranch = extractDefaultBranch(out.stdout) ?? "HEAD";
14550
+ const parsed = parseLsRemoteOutput(out.stdout);
14551
+ const { branches, tags } = splitRefs(parsed);
14552
+ return { branches, tags, default: defaultBranch };
14553
+ } catch (error) {
14554
+ const message = error instanceof Error ? error.message : "Failed to list refs";
14555
+ throw new RpcError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, message);
14556
+ }
14557
+ };
14558
+
13861
14559
  // src/handlers/index.ts
13862
- var createFsHandlers = (allowedPaths) => ({
14560
+ var createDaemonHandlers = (allowedPaths, workDir) => ({
13863
14561
  "fs/listTree": createFsListTreeHandler(allowedPaths),
13864
14562
  "fs/dirScan": createFsDirScanHandler(allowedPaths),
13865
14563
  "fs/exists": createFsExistsHandler(allowedPaths),
@@ -13869,7 +14567,10 @@ var createFsHandlers = (allowedPaths) => ({
13869
14567
  "fs/repoStatus": createFsRepoStatusHandler(allowedPaths),
13870
14568
  "fs/commitExists": createFsCommitExistsHandler(allowedPaths),
13871
14569
  "fs/repoTree": createFsRepoTreeHandler(allowedPaths),
13872
- "fs/repoScan": createFsRepoScanHandler(allowedPaths)
14570
+ "fs/repoScan": createFsRepoScanHandler(allowedPaths),
14571
+ "config/setAuth": createConfigSetAuthHandler(workDir),
14572
+ "vcs/clone": createVcsCloneHandler(workDir),
14573
+ "vcs/refs": createVcsRefsHandler(workDir)
13873
14574
  });
13874
14575
 
13875
14576
  // src/rpcHandler.ts
@@ -13889,8 +14590,10 @@ var buildError = (code, message, data) => ({
13889
14590
 
13890
14591
  class RpcHandler {
13891
14592
  handlers;
13892
- constructor(handlers) {
14593
+ context;
14594
+ constructor(handlers, context = {}) {
13893
14595
  this.handlers = new Map(Object.entries(handlers));
14596
+ this.context = context;
13894
14597
  }
13895
14598
  async handleMessage(raw) {
13896
14599
  let payload;
@@ -13929,7 +14632,7 @@ class RpcHandler {
13929
14632
  };
13930
14633
  }
13931
14634
  try {
13932
- const result = await handler(request.params);
14635
+ const result = await handler(request.params, this.context);
13933
14636
  if (request.id === undefined) {
13934
14637
  return null;
13935
14638
  }
@@ -13962,6 +14665,7 @@ class WebSocketClient {
13962
14665
  onError;
13963
14666
  reconnectMinDelayMs;
13964
14667
  reconnectMaxDelayMs;
14668
+ maxReconnectAttempts;
13965
14669
  socket = null;
13966
14670
  shouldReconnect = true;
13967
14671
  reconnectAttempt = 0;
@@ -13975,6 +14679,7 @@ class WebSocketClient {
13975
14679
  this.onError = options.onError;
13976
14680
  this.reconnectMinDelayMs = options.reconnectMinDelayMs ?? 1000;
13977
14681
  this.reconnectMaxDelayMs = options.reconnectMaxDelayMs ?? 30000;
14682
+ this.maxReconnectAttempts = options.maxReconnectAttempts ?? 64;
13978
14683
  }
13979
14684
  connect() {
13980
14685
  this.shouldReconnect = true;
@@ -14086,6 +14791,11 @@ class WebSocketClient {
14086
14791
  this.socket.send(JSON.stringify(response));
14087
14792
  }
14088
14793
  scheduleReconnect() {
14794
+ if (this.maxReconnectAttempts > 0 && this.reconnectAttempt >= this.maxReconnectAttempts) {
14795
+ console.error(`[WebSocketClient] Max reconnect attempts (${this.maxReconnectAttempts}) reached, giving up`);
14796
+ this.shouldReconnect = false;
14797
+ return;
14798
+ }
14089
14799
  const delay = Math.min(this.reconnectMaxDelayMs, this.reconnectMinDelayMs * 2 ** this.reconnectAttempt);
14090
14800
  this.reconnectAttempt += 1;
14091
14801
  setTimeout(() => {
@@ -14105,7 +14815,7 @@ class WebSocketClient {
14105
14815
 
14106
14816
  // src/index.ts
14107
14817
  import { readFileSync as readFileSync2 } from "node:fs";
14108
- import { join, dirname } from "node:path";
14818
+ import { join, dirname, resolve } from "node:path";
14109
14819
  import { fileURLToPath as fileURLToPath2 } from "node:url";
14110
14820
  function readVersion() {
14111
14821
  try {
@@ -14121,13 +14831,24 @@ function readVersion() {
14121
14831
  return "unknown";
14122
14832
  }
14123
14833
  var program2 = new Command;
14124
- program2.name("c4a-daemon").description("C4A daemon for local filesystem access").version(readVersion(), "-V, --version").option("--server <host>", "Server host (e.g. localhost:5100, example.com/prefix)", DEFAULT_SERVER_HOST).option("--allow <paths>", "Comma-separated allowed paths (default: all directories)").parse(process.argv);
14834
+ program2.name("c4a-daemon").description("C4A daemon for local filesystem access").version(readVersion(), "-V, --version").option("--server <host>", "Server host (e.g. localhost:5100, example.com/prefix)", DEFAULT_SERVER_HOST).option("--allow <paths>", "Comma-separated allowed paths (default: all directories)").option("--machine-id <id>", "Override daemon machine id (default: hostname)").option("--work-dir <dir>", "Working directory for daemon operations").parse(process.argv);
14125
14835
  var options = program2.opts();
14126
14836
  var serverUrl = resolveServerUrl(options.server);
14127
- var allowedPaths = parseAllowedPaths(options.allow);
14837
+ var resolvedWorkDir = options.workDir ? resolve(options.workDir) : undefined;
14838
+ var allowedPaths = parseAllowedPaths(options.allow, resolvedWorkDir);
14128
14839
  console.log(`Server: ${serverUrl}`);
14129
14840
  console.log(`Allowed: ${allowedPaths.join(", ")}`);
14130
- var rpcHandler = new RpcHandler(createFsHandlers(allowedPaths));
14841
+ if (resolvedWorkDir) {
14842
+ console.log(`WorkDir: ${resolvedWorkDir}`);
14843
+ }
14844
+ var client;
14845
+ var rpcHandler = new RpcHandler(createDaemonHandlers(allowedPaths, resolvedWorkDir), {
14846
+ notify: (method, params) => {
14847
+ try {
14848
+ client.sendNotification(method, params);
14849
+ } catch {}
14850
+ }
14851
+ });
14131
14852
  var heartbeatTimer = null;
14132
14853
  var stopHeartbeat = () => {
14133
14854
  if (heartbeatTimer) {
@@ -14135,18 +14856,18 @@ var stopHeartbeat = () => {
14135
14856
  heartbeatTimer = null;
14136
14857
  }
14137
14858
  };
14138
- var startHeartbeat = (client, machineId) => {
14859
+ var startHeartbeat = (client2, machineId) => {
14139
14860
  stopHeartbeat();
14140
14861
  heartbeatTimer = setInterval(() => {
14141
- client.sendNotification("daemon/heartbeat", { machine_id: machineId, timestamp: Date.now() });
14862
+ client2.sendNotification("daemon/heartbeat", { machine_id: machineId, timestamp: Date.now() });
14142
14863
  }, DAEMON_HEARTBEAT_INTERVAL);
14143
14864
  };
14144
- var client = new WebSocketClient({
14865
+ client = new WebSocketClient({
14145
14866
  url: serverUrl,
14146
14867
  onRequest: (request) => rpcHandler.handleRequest(request),
14147
14868
  onOpen: async () => {
14148
14869
  try {
14149
- const handshakePayload = buildHandshakePayload(allowedPaths);
14870
+ const handshakePayload = buildHandshakePayload(allowedPaths, options.machineId);
14150
14871
  const response = await client.sendRpcRequest("daemon/handshake", handshakePayload);
14151
14872
  if ("error" in response) {
14152
14873
  console.error(`Handshake error: ${response.error.message}`);