common-qa-mcp 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/remoteMcp/adapter.js +1 -1
- package/dist/adapters/simplifyqa/adapter.d.ts +37 -10
- package/dist/adapters/simplifyqa/adapter.d.ts.map +1 -1
- package/dist/adapters/simplifyqa/adapter.js +180 -40
- package/dist/adapters/simplifyqa/adapter.js.map +1 -1
- package/dist/domain/types.d.ts +3 -2
- package/dist/domain/types.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/tools/backlog.tools.d.ts.map +1 -1
- package/dist/tools/backlog.tools.js +130 -24
- package/dist/tools/backlog.tools.js.map +1 -1
- package/dist/tools/run.tools.d.ts.map +1 -1
- package/dist/tools/run.tools.js +6 -0
- package/dist/tools/run.tools.js.map +1 -1
- package/package.json +1 -1
|
@@ -38,7 +38,7 @@ export class RemoteMcpAdapter {
|
|
|
38
38
|
async client() {
|
|
39
39
|
if (!this.clientPromise) {
|
|
40
40
|
this.clientPromise = (async () => {
|
|
41
|
-
const client = new Client({ name: 'common-qa-mcp', version: '0.
|
|
41
|
+
const client = new Client({ name: 'common-qa-mcp', version: '0.5.0' });
|
|
42
42
|
// The `as Transport` cast bridges exactOptionalPropertyTypes: StreamableHTTPClientTransport
|
|
43
43
|
// implements Transport, but the SDK's own .d.ts (built without EOPT) leaves its optional
|
|
44
44
|
// callbacks (onclose?/onerror?/onmessage?) without `| undefined`, which this project's EOPT
|
|
@@ -22,6 +22,7 @@ export declare class SimplifyQaAdapter {
|
|
|
22
22
|
private readonly http;
|
|
23
23
|
private agentHop;
|
|
24
24
|
private formulaCache;
|
|
25
|
+
private backlogDefaultsCache;
|
|
25
26
|
constructor(cfg: ConnectedQaConfig, transport?: SimplifyQaTransport);
|
|
26
27
|
private headers;
|
|
27
28
|
private requireProjectId;
|
|
@@ -89,6 +90,23 @@ export declare class SimplifyQaAdapter {
|
|
|
89
90
|
listDefects(q: DefectQuery): Promise<ListResult<DefectSummary>>;
|
|
90
91
|
/** `POST /pm/task/search` — same searchFields protocol as defect/userstory search. */
|
|
91
92
|
listTasks(q: TaskQuery): Promise<ListResult<TaskSummary>>;
|
|
93
|
+
/** Fetch the project's default `state` and `priority` for a backlog entity.
|
|
94
|
+
* parentId: 1 = Epic, 2 = Feature, 3 = User Story. Two-source resolution, in order:
|
|
95
|
+
*
|
|
96
|
+
* 1. `/pm/fields/:parentId?fieldType=defaultFld` — pick the field value marked `isDefault: true`;
|
|
97
|
+
* fall back to the first non-archived value (seed ships with `isDefault: false` everywhere
|
|
98
|
+
* until a customer configures one).
|
|
99
|
+
* 2. If either state or priority is still missing, hit the entity's `/search` endpoint for one
|
|
100
|
+
* existing row in the project and steal its state / priority. Real data is a more reliable
|
|
101
|
+
* signal than the fields config, which is customer-scoped for Epic/Feature (parentIds 1/2)
|
|
102
|
+
* and can be missing entirely on customers that pre-date the seeding step.
|
|
103
|
+
*
|
|
104
|
+
* Returns the value's `.key` (uppercase enum like `PROPOSED`, `MEDIUM`) — that is what the entity
|
|
105
|
+
* row stores; the field metadata declares `keyname: "key"` / `type: "KEY"` and the SQA UI binding
|
|
106
|
+
* (see epic-form.component.html: `keyname="key" type="KEY" [displayKeys]="{'field':'value'}"`) confirms
|
|
107
|
+
* key is stored, `.value` is only for display. Both sources are wrapped in try/catch: a transient
|
|
108
|
+
* upstream error must not block create; the backend surfaces its own error if invalid. */
|
|
109
|
+
private getBacklogDefaults;
|
|
92
110
|
createStory(input: StoryInput): Promise<SavedRef>;
|
|
93
111
|
updateStory(id: string, patch: StoryUpdate): Promise<SavedRef>;
|
|
94
112
|
listEpics(q: EpicQuery): Promise<ListResult<EpicSummary>>;
|
|
@@ -411,21 +429,30 @@ export declare class SimplifyQaAdapter {
|
|
|
411
429
|
startRun(target: RunTarget, opts: RunOptions): Promise<RunRef>;
|
|
412
430
|
stopRun(runId: string): Promise<void>;
|
|
413
431
|
listCloudRunners(): Promise<CloudRunner[]>;
|
|
432
|
+
/** Gather every identifier this process can claim: bare hostname, non-loopback MAC addresses,
|
|
433
|
+
* non-loopback IPs. Cached per-adapter — network interfaces do not change mid-session. */
|
|
434
|
+
private localIdsCache;
|
|
435
|
+
private getLocalIdentifiers;
|
|
436
|
+
/** Does a machineRegistration row belong to the machine this MCP is running on?
|
|
437
|
+
* A single positive match on hostname, MAC, or IP is enough — the local agent registers with
|
|
438
|
+
* ALL of these, but the surviving match on the SQA row depends on OS quirks (Windows writes a
|
|
439
|
+
* padded hostname, macOS writes `foo.local`, IPs shift on Wi-Fi handoff). Any one match is
|
|
440
|
+
* proof enough; we don't try to reconcile the others. */
|
|
441
|
+
private isThisMachine;
|
|
414
442
|
listLocalAgents(): Promise<LocalAgent[]>;
|
|
415
443
|
/**
|
|
416
|
-
* Pick THE ACTING USER'S OWN
|
|
417
|
-
*
|
|
418
|
-
*
|
|
444
|
+
* Pick THE ACTING USER'S OWN agent — never row [0] of a shared-tenant roster (which would
|
|
445
|
+
* silently dispatch a local run to a teammate's box, and the run would sit queued until TTL
|
|
446
|
+
* because it lands in a Redis key that no local agent on the caller's machine polls).
|
|
419
447
|
*
|
|
420
448
|
* SQA's `machineRegistration` row carries no owner_user_id column (unlike walnut, which matches
|
|
421
|
-
* the JWT `id` claim to `owner_user_id`), so we identify "your" agent by
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
* `
|
|
425
|
-
* Zero/many matches refuse honestly; a silent guess would just repeat the exact bug we're fixing.
|
|
449
|
+
* the JWT `id` claim to `owner_user_id`), so we identify "your" agent by physical identity:
|
|
450
|
+
* hostname, MAC address, or IP — any one match against `os.networkInterfaces()` on the box this
|
|
451
|
+
* MCP is running on. When exactly one row is "this machine", pick it regardless of the DB
|
|
452
|
+
* `active` flag (that flag is stale by design — see listLocalAgents for the executionQueue bug).
|
|
426
453
|
*
|
|
427
|
-
* ponytail:
|
|
428
|
-
* to machineRegistration (mirror walnut's `resolveLocalAgentCredential` at that point).
|
|
454
|
+
* ponytail: multi-identifier heuristic. Upgrade to owner-user-id match if the backend adds that
|
|
455
|
+
* column to machineRegistration (mirror walnut's `resolveLocalAgentCredential` at that point).
|
|
429
456
|
*/
|
|
430
457
|
private resolveLocalAgentId;
|
|
431
458
|
private detectExecutionOS;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/simplifyqa/adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAU,MAAM,YAAY,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EACV,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAC5F,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAC5D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAClE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EACxF,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EACpE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAChF,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,uBAAuB,EACjF,6BAA6B,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EACnE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EACxD,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EACpF,WAAW,EAAE,kBAAkB,EAAE,mBAAmB,EACrD,MAAM,uBAAuB,CAAC;AAY/B,kFAAkF;AAClF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,cAAc,GAAG,UAAU,CAAC,CAAC;AA0NrG;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAiB;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/simplifyqa/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -231,6 +231,9 @@ export class SimplifyQaAdapter {
|
|
|
231
231
|
// (from the Promise.all in saveGeneratedIterations) all await the same fetch instead of each firing
|
|
232
232
|
// their own /tm/syncFormulas/fetch before the cache is populated.
|
|
233
233
|
this.formulaCache = null;
|
|
234
|
+
// Same rationale for the backlog default state/priority per parentId (1=Epic, 2=Feature, 3=Story).
|
|
235
|
+
// Fields config barely changes mid-session; cache the in-flight promise so parallel creates share it.
|
|
236
|
+
this.backlogDefaultsCache = new Map();
|
|
234
237
|
// ---------------------------------------------------------------------- Automation resolvers
|
|
235
238
|
/** POST /tm/action/search → per (technology, name) → row (id + objectRequired + objectTemplate).
|
|
236
239
|
* Cached per adapter instance: the catalog is customer-wide and rarely changes during a session.
|
|
@@ -244,6 +247,9 @@ export class SimplifyQaAdapter {
|
|
|
244
247
|
* formulaCache. Populated from `POST /tm/objectTemplate/search` (confirmed endpoint,
|
|
245
248
|
* api-endpoints.ts `object.search`'s sibling `objectTemplate.search`). */
|
|
246
249
|
this.objectTemplateCache = null;
|
|
250
|
+
/** Gather every identifier this process can claim: bare hostname, non-loopback MAC addresses,
|
|
251
|
+
* non-loopback IPs. Cached per-adapter — network interfaces do not change mid-session. */
|
|
252
|
+
this.localIdsCache = null;
|
|
247
253
|
if (!cfg.baseUrl) {
|
|
248
254
|
throw new Error('SimplifyQaAdapter requires a baseUrl — set QA_BASE_URL or use a JWT with a `url` claim.');
|
|
249
255
|
}
|
|
@@ -738,12 +744,102 @@ export class SimplifyQaAdapter {
|
|
|
738
744
|
const rows = pickArray(raw, 'data', 'content', 'tasks');
|
|
739
745
|
return withTotal(rows.map(toTaskSummary), raw);
|
|
740
746
|
}
|
|
747
|
+
// ---------------------------------------------------------------------- Backlog defaults
|
|
748
|
+
/** Fetch the project's default `state` and `priority` for a backlog entity.
|
|
749
|
+
* parentId: 1 = Epic, 2 = Feature, 3 = User Story. Two-source resolution, in order:
|
|
750
|
+
*
|
|
751
|
+
* 1. `/pm/fields/:parentId?fieldType=defaultFld` — pick the field value marked `isDefault: true`;
|
|
752
|
+
* fall back to the first non-archived value (seed ships with `isDefault: false` everywhere
|
|
753
|
+
* until a customer configures one).
|
|
754
|
+
* 2. If either state or priority is still missing, hit the entity's `/search` endpoint for one
|
|
755
|
+
* existing row in the project and steal its state / priority. Real data is a more reliable
|
|
756
|
+
* signal than the fields config, which is customer-scoped for Epic/Feature (parentIds 1/2)
|
|
757
|
+
* and can be missing entirely on customers that pre-date the seeding step.
|
|
758
|
+
*
|
|
759
|
+
* Returns the value's `.key` (uppercase enum like `PROPOSED`, `MEDIUM`) — that is what the entity
|
|
760
|
+
* row stores; the field metadata declares `keyname: "key"` / `type: "KEY"` and the SQA UI binding
|
|
761
|
+
* (see epic-form.component.html: `keyname="key" type="KEY" [displayKeys]="{'field':'value'}"`) confirms
|
|
762
|
+
* key is stored, `.value` is only for display. Both sources are wrapped in try/catch: a transient
|
|
763
|
+
* upstream error must not block create; the backend surfaces its own error if invalid. */
|
|
764
|
+
async getBacklogDefaults(parentId) {
|
|
765
|
+
const cached = this.backlogDefaultsCache.get(parentId);
|
|
766
|
+
if (cached)
|
|
767
|
+
return cached;
|
|
768
|
+
const projectId = this.requireProjectId();
|
|
769
|
+
const p = (async () => {
|
|
770
|
+
const out = {};
|
|
771
|
+
// Source 1: /pm/fields config.
|
|
772
|
+
try {
|
|
773
|
+
const raw = await this.http.getJson(`/pm/fields/${parentId}?fieldType=defaultFld&projectId=${projectId}`);
|
|
774
|
+
const fields = Array.isArray(raw) ? raw : pickArray(raw, 'data', 'content', 'fields');
|
|
775
|
+
const pick = (key) => {
|
|
776
|
+
const field = fields.find((f) => f?.key === key);
|
|
777
|
+
const values = Array.isArray(field?.values) ? field.values : [];
|
|
778
|
+
const chosen = values.find((v) => v?.isDefault === true && v?.archive !== true)
|
|
779
|
+
?? values.find((v) => v?.archive !== true);
|
|
780
|
+
// Entity rows persist the uppercase enum (value.key). value.value is display-only.
|
|
781
|
+
const stored = str(chosen ?? {}, 'key', 'value');
|
|
782
|
+
return stored && stored.length > 0 ? stored : undefined;
|
|
783
|
+
};
|
|
784
|
+
const s = pick('state');
|
|
785
|
+
const pr = pick('priority');
|
|
786
|
+
if (s)
|
|
787
|
+
out.state = s;
|
|
788
|
+
if (pr)
|
|
789
|
+
out.priority = pr;
|
|
790
|
+
}
|
|
791
|
+
catch { /* fall through to Source 2 */ }
|
|
792
|
+
// Source 2: fall back to state/priority from an existing entity of the same kind in this project.
|
|
793
|
+
// Epic has no priority field, so we only need state for parentId=1.
|
|
794
|
+
const needsPriority = parentId !== 1 && !out.priority;
|
|
795
|
+
if (!out.state || needsPriority) {
|
|
796
|
+
try {
|
|
797
|
+
const path = parentId === 1 ? '/pm/epic/search'
|
|
798
|
+
: parentId === 2 ? '/pm/feature/search'
|
|
799
|
+
: '/pm/userstory/search';
|
|
800
|
+
// Story rows carry a scalar `projectId`; Epic/Feature rows carry an array `projectIds`.
|
|
801
|
+
const filters = [
|
|
802
|
+
{ column: 'deleted', value: false },
|
|
803
|
+
parentId === 3
|
|
804
|
+
? { column: 'projectId', value: projectId }
|
|
805
|
+
: { column: 'projectIds', value: projectId },
|
|
806
|
+
];
|
|
807
|
+
const body = this.searchBody(filters, { startIndex: 0, limit: 1 });
|
|
808
|
+
const raw = await this.http.postJson(path, body);
|
|
809
|
+
const rows = pickArray(raw, 'data', 'content');
|
|
810
|
+
const row = rows[0];
|
|
811
|
+
if (row) {
|
|
812
|
+
if (!out.state) {
|
|
813
|
+
const s = str(row, 'state', 'status');
|
|
814
|
+
if (s)
|
|
815
|
+
out.state = s;
|
|
816
|
+
}
|
|
817
|
+
if (needsPriority) {
|
|
818
|
+
const pr = str(row, 'priority');
|
|
819
|
+
if (pr)
|
|
820
|
+
out.priority = pr;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
catch { /* leave out.state / out.priority absent; caller can override */ }
|
|
825
|
+
}
|
|
826
|
+
return out;
|
|
827
|
+
})();
|
|
828
|
+
this.backlogDefaultsCache.set(parentId, p);
|
|
829
|
+
return p;
|
|
830
|
+
}
|
|
741
831
|
// ---------------------------------------------------------------------- Stories (write)
|
|
742
832
|
async createStory(input) {
|
|
833
|
+
const defaults = await this.getBacklogDefaults(3);
|
|
834
|
+
const state = input.state ?? defaults.state;
|
|
835
|
+
const priority = input.priority ?? defaults.priority;
|
|
743
836
|
const body = {
|
|
744
837
|
...input,
|
|
745
838
|
// SQA userstory expects a singular numeric projectId; fill from the connected project when omitted.
|
|
746
839
|
projectId: input.projectId ?? this.requireProjectId(),
|
|
840
|
+
...(state ? { state } : {}),
|
|
841
|
+
...(priority ? { priority } : {}),
|
|
842
|
+
customFields: input.customFields ?? {},
|
|
747
843
|
};
|
|
748
844
|
const raw = await this.http.postJson('/pm/userstory', body);
|
|
749
845
|
return toSavedRef(raw, true);
|
|
@@ -791,9 +887,13 @@ export class SimplifyQaAdapter {
|
|
|
791
887
|
return toEpic(doc && typeof doc === 'object' ? doc : {});
|
|
792
888
|
}
|
|
793
889
|
async createEpic(input) {
|
|
890
|
+
const defaults = await this.getBacklogDefaults(1);
|
|
891
|
+
const state = input.state ?? defaults.state;
|
|
794
892
|
const body = {
|
|
795
893
|
...input,
|
|
796
894
|
projectIds: input.projectIds ?? [this.requireProjectId()],
|
|
895
|
+
...(state ? { state } : {}),
|
|
896
|
+
customFields: input.customFields ?? {},
|
|
797
897
|
};
|
|
798
898
|
const raw = await this.http.postJson('/pm/epic', body);
|
|
799
899
|
return toSavedRef(raw, true);
|
|
@@ -855,9 +955,15 @@ export class SimplifyQaAdapter {
|
|
|
855
955
|
return toFeature(doc && typeof doc === 'object' ? doc : {});
|
|
856
956
|
}
|
|
857
957
|
async createFeature(input) {
|
|
958
|
+
const defaults = await this.getBacklogDefaults(2);
|
|
959
|
+
const state = input.state ?? defaults.state;
|
|
960
|
+
const priority = input.priority ?? defaults.priority;
|
|
858
961
|
const body = {
|
|
859
962
|
...input,
|
|
860
963
|
projectIds: input.projectIds ?? [this.requireProjectId()],
|
|
964
|
+
...(state ? { state } : {}),
|
|
965
|
+
...(priority ? { priority } : {}),
|
|
966
|
+
customFields: input.customFields ?? {},
|
|
861
967
|
};
|
|
862
968
|
const raw = await this.http.postJson('/pm/feature', body);
|
|
863
969
|
return toSavedRef(raw, true);
|
|
@@ -2955,6 +3061,46 @@ export class SimplifyQaAdapter {
|
|
|
2955
3061
|
};
|
|
2956
3062
|
});
|
|
2957
3063
|
}
|
|
3064
|
+
getLocalIdentifiers() {
|
|
3065
|
+
if (this.localIdsCache)
|
|
3066
|
+
return this.localIdsCache;
|
|
3067
|
+
const hostFull = os.hostname().toLowerCase();
|
|
3068
|
+
const hostBare = hostFull.replace(/\.(local|lan|localdomain)$/, '').split('.')[0] ?? hostFull;
|
|
3069
|
+
const macs = new Set();
|
|
3070
|
+
const ips = new Set();
|
|
3071
|
+
const ifaces = os.networkInterfaces();
|
|
3072
|
+
for (const list of Object.values(ifaces)) {
|
|
3073
|
+
if (!list)
|
|
3074
|
+
continue;
|
|
3075
|
+
for (const iface of list) {
|
|
3076
|
+
if (iface.internal)
|
|
3077
|
+
continue;
|
|
3078
|
+
if (iface.mac && iface.mac !== '00:00:00:00:00:00')
|
|
3079
|
+
macs.add(iface.mac.toLowerCase());
|
|
3080
|
+
if (iface.address)
|
|
3081
|
+
ips.add(iface.address);
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
this.localIdsCache = { hostBare, macs, ips };
|
|
3085
|
+
return this.localIdsCache;
|
|
3086
|
+
}
|
|
3087
|
+
/** Does a machineRegistration row belong to the machine this MCP is running on?
|
|
3088
|
+
* A single positive match on hostname, MAC, or IP is enough — the local agent registers with
|
|
3089
|
+
* ALL of these, but the surviving match on the SQA row depends on OS quirks (Windows writes a
|
|
3090
|
+
* padded hostname, macOS writes `foo.local`, IPs shift on Wi-Fi handoff). Any one match is
|
|
3091
|
+
* proof enough; we don't try to reconcile the others. */
|
|
3092
|
+
isThisMachine(row, ids = this.getLocalIdentifiers()) {
|
|
3093
|
+
const name = (str(row, 'machineName', 'name', 'host') ?? '').toLowerCase();
|
|
3094
|
+
if (ids.hostBare.length > 0 && name.length > 0 && name.includes(ids.hostBare))
|
|
3095
|
+
return true;
|
|
3096
|
+
const mac = (str(row, 'macAddress') ?? '').toLowerCase();
|
|
3097
|
+
if (mac.length > 0 && ids.macs.has(mac))
|
|
3098
|
+
return true;
|
|
3099
|
+
const ip = str(row, 'ipAddress', 'hostAddress') ?? '';
|
|
3100
|
+
if (ip.length > 0 && ids.ips.has(ip))
|
|
3101
|
+
return true;
|
|
3102
|
+
return false;
|
|
3103
|
+
}
|
|
2958
3104
|
async listLocalAgents() {
|
|
2959
3105
|
// ponytail: no projectId filter — machine_registration.projectId is never written by
|
|
2960
3106
|
// test-management/services/machine_registration.service.js (create only stamps
|
|
@@ -2963,50 +3109,43 @@ export class SimplifyQaAdapter {
|
|
|
2963
3109
|
const body = this.searchBody([{ column: 'deleted', value: false }], { limit: 1000 });
|
|
2964
3110
|
const raw = await this.http.postJson('/tm/machineRegistration/search', body);
|
|
2965
3111
|
const rows = pickArray(raw, 'data', 'content', 'agents', 'machines');
|
|
2966
|
-
// ponytail:
|
|
2967
|
-
//
|
|
2968
|
-
//
|
|
2969
|
-
//
|
|
2970
|
-
// is
|
|
2971
|
-
//
|
|
2972
|
-
|
|
2973
|
-
// Redis-presence endpoint (ZSCORE SQAAGENTTRACKER), consult that here instead.
|
|
2974
|
-
const hostFull = os.hostname().toLowerCase();
|
|
2975
|
-
// Bare hostname: strip trailing .local/.lan/.localdomain AND take the first label. Handles
|
|
2976
|
-
// both directions of "short vs FQDN" mismatch — the agent may register as `Pratiks-MBP`
|
|
2977
|
-
// while os.hostname() returns `pratiks-mbp.local`, or vice-versa.
|
|
2978
|
-
const hostBare = hostFull.replace(/\.(local|lan|localdomain)$/, '').split('.')[0] ?? hostFull;
|
|
3112
|
+
// ponytail: mirror sqa-ui/machine-registration.component.ts:169. The DB `active` flag is only
|
|
3113
|
+
// written to `true` on FIRST registration (fire-and-forget in executions/services/
|
|
3114
|
+
// executionQueue.service.js:49) and back to `false` by a 7s-stale cron. Every subsequent poll
|
|
3115
|
+
// is a no-op for the DB — a still-polling agent frequently shows `active:false`. Executions
|
|
3116
|
+
// service is off-limits for the fix, so when the row belongs to THIS machine (any of hostname
|
|
3117
|
+
// / MAC / IP matches), trust that over the stale DB flag.
|
|
3118
|
+
const ids = this.getLocalIdentifiers();
|
|
2979
3119
|
return rows.map((n) => {
|
|
2980
3120
|
const id = str(n, 'id', '_id', 'agentId') ?? '';
|
|
2981
3121
|
const agentName = str(n, 'machineName', 'name', 'host');
|
|
2982
3122
|
const owner = str(n, 'owner', 'userName', 'createdBy');
|
|
2983
3123
|
const hostAddr = str(n, 'macAddress', 'hostAddress');
|
|
2984
3124
|
const dbActive = typeof n?.active === 'boolean' ? n.active : false;
|
|
2985
|
-
const
|
|
2986
|
-
const isMyBox = nameLc.length > 0 && hostBare.length > 0 && nameLc.includes(hostBare);
|
|
3125
|
+
const isMyBox = this.isThisMachine(n, ids);
|
|
2987
3126
|
return {
|
|
2988
3127
|
id,
|
|
2989
3128
|
...(agentName ? { name: agentName } : {}),
|
|
2990
3129
|
online: dbActive || isMyBox,
|
|
3130
|
+
...(isMyBox ? { isThisMachine: true } : {}),
|
|
2991
3131
|
...(owner ? { owner } : {}),
|
|
2992
3132
|
...(hostAddr ? { host: hostAddr } : {}),
|
|
2993
3133
|
};
|
|
2994
3134
|
});
|
|
2995
3135
|
}
|
|
2996
3136
|
/**
|
|
2997
|
-
* Pick THE ACTING USER'S OWN
|
|
2998
|
-
*
|
|
2999
|
-
*
|
|
3137
|
+
* Pick THE ACTING USER'S OWN agent — never row [0] of a shared-tenant roster (which would
|
|
3138
|
+
* silently dispatch a local run to a teammate's box, and the run would sit queued until TTL
|
|
3139
|
+
* because it lands in a Redis key that no local agent on the caller's machine polls).
|
|
3000
3140
|
*
|
|
3001
3141
|
* SQA's `machineRegistration` row carries no owner_user_id column (unlike walnut, which matches
|
|
3002
|
-
* the JWT `id` claim to `owner_user_id`), so we identify "your" agent by
|
|
3003
|
-
*
|
|
3004
|
-
*
|
|
3005
|
-
* `
|
|
3006
|
-
* Zero/many matches refuse honestly; a silent guess would just repeat the exact bug we're fixing.
|
|
3142
|
+
* the JWT `id` claim to `owner_user_id`), so we identify "your" agent by physical identity:
|
|
3143
|
+
* hostname, MAC address, or IP — any one match against `os.networkInterfaces()` on the box this
|
|
3144
|
+
* MCP is running on. When exactly one row is "this machine", pick it regardless of the DB
|
|
3145
|
+
* `active` flag (that flag is stale by design — see listLocalAgents for the executionQueue bug).
|
|
3007
3146
|
*
|
|
3008
|
-
* ponytail:
|
|
3009
|
-
* to machineRegistration (mirror walnut's `resolveLocalAgentCredential` at that point).
|
|
3147
|
+
* ponytail: multi-identifier heuristic. Upgrade to owner-user-id match if the backend adds that
|
|
3148
|
+
* column to machineRegistration (mirror walnut's `resolveLocalAgentCredential` at that point).
|
|
3010
3149
|
*/
|
|
3011
3150
|
async resolveLocalAgentId(preferred) {
|
|
3012
3151
|
if (preferred && preferred.length > 0)
|
|
@@ -3016,24 +3155,25 @@ export class SimplifyQaAdapter {
|
|
|
3016
3155
|
throw new Error('startRun (LOCAL): no local agent registered. Register a machine in the SimplifyQA UI ' +
|
|
3017
3156
|
'first, or pass mode: "cloud" if a cloud runner is provisioned.');
|
|
3018
3157
|
}
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
}
|
|
3024
|
-
const hostFull = os.hostname().toLowerCase();
|
|
3025
|
-
// Same short-vs-FQDN handling as listLocalAgents — see the ponytail note there.
|
|
3026
|
-
const host = hostFull.replace(/\.(local|lan|localdomain)$/, '').split('.')[0] ?? hostFull;
|
|
3027
|
-
const mine = active.filter((a) => host.length > 0 && (a.name ?? '').toLowerCase().includes(host));
|
|
3158
|
+
// Priority 1: rows that physically match this machine. Ignore the DB `active` flag — a running
|
|
3159
|
+
// agent frequently shows `active:false` between the 5s stale-cron and the next heartbeat, and
|
|
3160
|
+
// the executions service is off-limits for the real fix.
|
|
3161
|
+
const mine = agents.filter((a) => a.isThisMachine);
|
|
3028
3162
|
if (mine.length === 1)
|
|
3029
3163
|
return mine[0].id;
|
|
3030
|
-
const listing = (arr) => arr.map((a) => ` - ${a.name ?? a.id} \`${a.id}\``).join('\n');
|
|
3031
3164
|
if (mine.length > 1) {
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3165
|
+
const listing = mine.map((a) => ` - ${a.name ?? a.id} \`${a.id}\``).join('\n');
|
|
3166
|
+
throw new Error(`startRun (LOCAL): multiple agents registered from this machine. Pass \`agentId\` to ` +
|
|
3167
|
+
`pick one:\n${listing}`);
|
|
3168
|
+
}
|
|
3169
|
+
// No physical match — fall back to the old online-only pool (roster shared across teammates).
|
|
3170
|
+
// No physical match for this machine. Refuse to guess — dispatching to a teammate's agent is
|
|
3171
|
+
// the exact silent-misroute bug isThisMachine was written to prevent. Surface the roster so
|
|
3172
|
+
// the caller can pass `agentId` explicitly.
|
|
3173
|
+
const listing = agents.map((a) => ` - ${a.name ?? a.id} \`${a.id}\`${a.online ? '' : ' (offline)'}`).join('\n');
|
|
3174
|
+
throw new Error(`startRun (LOCAL): no agent registered from this machine (hostname / MAC / IP did not match ` +
|
|
3175
|
+
`any row). Launch the SimplifyQA desktop agent on this box, or pass \`agentId\` explicitly. ` +
|
|
3176
|
+
`Registered agents:\n${listing}`);
|
|
3037
3177
|
}
|
|
3038
3178
|
detectExecutionOS() {
|
|
3039
3179
|
if (process.platform === 'win32')
|