@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
201 lines • 10 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import { getIssue, listIssueComments, listTeamCycles, listTeamDocuments, listTeamIssues, listTeamLabels, listTeamMembers, listTeamProjects, listTeams, resolveTeam, searchIssues, } from "../../../../core/linear/client.js";
import { normalizeComment, normalizeCycle, normalizeDocument, normalizeIssue, normalizeLabel, normalizeProject, normalizeTeam, normalizeUser, projectIssueRows, } from "../../../../core/linear/normalize.js";
import { jsonBytes } from "../../../../core/render/json.js";
import { enoent } from "../../../../errors/fs.js";
import { IOResult } from "../../../../io/types.js";
import { FlagView } from "../../../spec/flag_view.js";
import { pickString } from "../../../../utils/records.js";
import { firstText, resolveIssue } from "./util.js";
function requireTeam(fl) {
const team = fl.asStr('team');
if (team === undefined || team === '')
throw new Error('--team is required');
return team;
}
async function allUsers(accessor) {
const teams = await listTeams(accessor.transport);
const seen = new Set();
const users = [];
for (const team of teams) {
const teamId = typeof team.id === 'string' ? team.id : '';
for (const user of await listTeamMembers(accessor.transport, teamId)) {
const uid = user.id;
if (typeof uid !== 'string' || seen.has(uid))
continue;
seen.add(uid);
users.push(user);
}
}
return users;
}
export async function teamList(accessor, _paths, _texts, _ctx) {
let teams = await listTeams(accessor.transport);
if (accessor.teamIds !== null && accessor.teamIds.length > 0) {
const keep = new Set(accessor.teamIds);
teams = teams.filter((team) => typeof team.id === 'string' && keep.has(team.id));
}
return [jsonBytes(teams.map((team) => normalizeTeam(team))), new IOResult()];
}
export async function teamGet(accessor, _paths, texts, _ctx) {
const team = await resolveTeam(accessor.transport, firstText(texts, 'team key'));
return [jsonBytes(normalizeTeam(team)), new IOResult()];
}
export async function teamMembers(accessor, _paths, texts, _ctx) {
const team = await resolveTeam(accessor.transport, firstText(texts, 'team key'));
const teamId = typeof team.id === 'string' ? team.id : '';
const users = await listTeamMembers(accessor.transport, teamId);
return [jsonBytes(users.map((user) => normalizeUser(user))), new IOResult()];
}
export async function issueList(accessor, _paths, _texts, ctx) {
const team = await resolveTeam(accessor.transport, requireTeam(new FlagView(ctx.flags)));
const teamId = typeof team.id === 'string' ? team.id : '';
const issues = await listTeamIssues(accessor.transport, teamId);
return [jsonBytes(issues.map((issue) => normalizeIssue(issue))), new IOResult()];
}
export async function issueGet(accessor, _paths, texts, _ctx) {
const issueId = await resolveIssue(accessor, firstText(texts, 'issue key'));
const issue = await getIssue(accessor.transport, issueId);
return [jsonBytes(normalizeIssue(issue)), new IOResult()];
}
export async function projectList(accessor, _paths, _texts, ctx) {
const team = await resolveTeam(accessor.transport, requireTeam(new FlagView(ctx.flags)));
const teamId = typeof team.id === 'string' ? team.id : '';
const projects = await listTeamProjects(accessor.transport, teamId);
const payload = [];
for (const project of projects) {
const projectId = typeof project.id === 'string' ? project.id : '';
const rows = projectIssueRows(await listTeamIssues(accessor.transport, teamId, undefined, [projectId]), projectId);
payload.push(normalizeProject(project, {
teamId,
teamKey: pickString(team, 'key'),
teamName: pickString(team, 'name'),
issues: rows,
}));
}
return [jsonBytes(payload), new IOResult()];
}
export async function projectGet(accessor, _paths, texts, ctx) {
const team = await resolveTeam(accessor.transport, requireTeam(new FlagView(ctx.flags)));
const teamId = typeof team.id === 'string' ? team.id : '';
const projectId = firstText(texts, 'project id');
const projects = await listTeamProjects(accessor.transport, teamId);
for (const project of projects) {
if (project.id === projectId) {
const rows = projectIssueRows(await listTeamIssues(accessor.transport, teamId, undefined, [projectId]), projectId);
return [
jsonBytes(normalizeProject(project, {
teamId,
teamKey: pickString(team, 'key'),
teamName: pickString(team, 'name'),
issues: rows,
})),
new IOResult(),
];
}
}
throw enoent(projectId);
}
export async function cycleList(accessor, _paths, _texts, ctx) {
const team = await resolveTeam(accessor.transport, requireTeam(new FlagView(ctx.flags)));
const teamId = typeof team.id === 'string' ? team.id : '';
const cycles = await listTeamCycles(accessor.transport, teamId);
return [jsonBytes(cycles.map((cycle) => normalizeCycle(cycle, teamId))), new IOResult()];
}
export async function cycleCurrent(accessor, _paths, _texts, ctx) {
const team = await resolveTeam(accessor.transport, requireTeam(new FlagView(ctx.flags)));
const teamId = typeof team.id === 'string' ? team.id : '';
const cycles = await listTeamCycles(accessor.transport, teamId);
let current;
for (const cycle of cycles) {
if (current === undefined) {
current = cycle;
continue;
}
const a = typeof cycle.number === 'number' ? cycle.number : 0;
const b = typeof current.number === 'number' ? current.number : 0;
if (a > b)
current = cycle;
}
if (current === undefined)
throw enoent('cycles');
return [jsonBytes(normalizeCycle(current, teamId)), new IOResult()];
}
export async function cycleGet(accessor, _paths, texts, ctx) {
const team = await resolveTeam(accessor.transport, requireTeam(new FlagView(ctx.flags)));
const teamId = typeof team.id === 'string' ? team.id : '';
const cycleId = firstText(texts, 'cycle id');
const cycles = await listTeamCycles(accessor.transport, teamId);
for (const cycle of cycles) {
if (cycle.id === cycleId)
return [jsonBytes(normalizeCycle(cycle, teamId)), new IOResult()];
}
throw enoent(cycleId);
}
export async function labelList(accessor, _paths, _texts, ctx) {
const team = await resolveTeam(accessor.transport, requireTeam(new FlagView(ctx.flags)));
const teamId = typeof team.id === 'string' ? team.id : '';
const labels = await listTeamLabels(accessor.transport, teamId);
return [jsonBytes(labels.map((label) => normalizeLabel(label))), new IOResult()];
}
export async function commentList(accessor, _paths, texts, _ctx) {
const issueId = await resolveIssue(accessor, firstText(texts, 'issue key'));
const issue = await getIssue(accessor.transport, issueId);
const issueKey = typeof issue.identifier === 'string' ? issue.identifier : null;
const comments = await listIssueComments(accessor.transport, issueId);
return [
jsonBytes(comments.map((comment) => normalizeComment(comment, issueId, issueKey))),
new IOResult(),
];
}
export async function userList(accessor, _paths, _texts, _ctx) {
const users = await allUsers(accessor);
return [jsonBytes(users.map((user) => normalizeUser(user))), new IOResult()];
}
export async function userGet(accessor, _paths, texts, _ctx) {
const email = firstText(texts, 'user email');
for (const user of await allUsers(accessor)) {
if (user.email === email)
return [jsonBytes(normalizeUser(user)), new IOResult()];
}
throw enoent(email);
}
export async function documentList(accessor, _paths, _texts, ctx) {
const team = await resolveTeam(accessor.transport, requireTeam(new FlagView(ctx.flags)));
const teamId = typeof team.id === 'string' ? team.id : '';
const documents = await listTeamDocuments(accessor.transport, teamId);
return [jsonBytes(documents.map((document) => normalizeDocument(document))), new IOResult()];
}
export async function documentGet(accessor, _paths, texts, ctx) {
const team = await resolveTeam(accessor.transport, requireTeam(new FlagView(ctx.flags)));
const teamId = typeof team.id === 'string' ? team.id : '';
const documentId = firstText(texts, 'document id');
const documents = await listTeamDocuments(accessor.transport, teamId);
for (const document of documents) {
if (document.id === documentId)
return [jsonBytes(normalizeDocument(document)), new IOResult()];
}
throw enoent(documentId);
}
export async function search(accessor, _paths, texts, ctx) {
const fl = new FlagView(ctx.flags);
const flagQuery = fl.asStr('query');
const query = flagQuery !== undefined && flagQuery !== '' ? flagQuery : (texts[0] ?? null);
if (query === null || query === '')
throw new Error('a search query is required');
const results = await searchIssues(accessor.transport, query);
return [jsonBytes(results), new IOResult()];
}
//# sourceMappingURL=reads.js.map