@cat-factory/integrations 0.141.2 → 0.142.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.
Files changed (33) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/modules/tasks/GitHubIssuesProvider.d.ts +8 -0
  6. package/dist/modules/tasks/GitHubIssuesProvider.d.ts.map +1 -1
  7. package/dist/modules/tasks/GitHubIssuesProvider.js +5 -0
  8. package/dist/modules/tasks/GitHubIssuesProvider.js.map +1 -1
  9. package/dist/modules/tasks/GitLabIssuesProvider.d.ts +126 -0
  10. package/dist/modules/tasks/GitLabIssuesProvider.d.ts.map +1 -0
  11. package/dist/modules/tasks/GitLabIssuesProvider.js +251 -0
  12. package/dist/modules/tasks/GitLabIssuesProvider.js.map +1 -0
  13. package/dist/modules/tasks/TaskConnectionService.d.ts +8 -6
  14. package/dist/modules/tasks/TaskConnectionService.d.ts.map +1 -1
  15. package/dist/modules/tasks/TaskConnectionService.js +64 -26
  16. package/dist/modules/tasks/TaskConnectionService.js.map +1 -1
  17. package/dist/modules/tasks/TaskImportService.d.ts +10 -4
  18. package/dist/modules/tasks/TaskImportService.d.ts.map +1 -1
  19. package/dist/modules/tasks/TaskImportService.js +19 -6
  20. package/dist/modules/tasks/TaskImportService.js.map +1 -1
  21. package/dist/modules/tasks/github-issues.logic.d.ts +10 -0
  22. package/dist/modules/tasks/github-issues.logic.d.ts.map +1 -1
  23. package/dist/modules/tasks/github-issues.logic.js +13 -0
  24. package/dist/modules/tasks/github-issues.logic.js.map +1 -1
  25. package/dist/modules/tasks/gitlab-issues.logic.d.ts +106 -0
  26. package/dist/modules/tasks/gitlab-issues.logic.d.ts.map +1 -0
  27. package/dist/modules/tasks/gitlab-issues.logic.js +186 -0
  28. package/dist/modules/tasks/gitlab-issues.logic.js.map +1 -0
  29. package/dist/modules/tasks/tasks.logic.d.ts +14 -8
  30. package/dist/modules/tasks/tasks.logic.d.ts.map +1 -1
  31. package/dist/modules/tasks/tasks.logic.js +17 -15
  32. package/dist/modules/tasks/tasks.logic.js.map +1 -1
  33. package/package.json +4 -4
@@ -0,0 +1,186 @@
1
+ // GitLab-issues task-source pure logic, the sibling of `github-issues.logic.ts`: the
2
+ // descriptor, the external-id grammar and its round-trip, the ref parser, and the
3
+ // project-scoped search query. Pure, so every rule below is unit-testable without a
4
+ // live API; the fetch itself lives in `GitLabIssuesProvider`.
5
+ //
6
+ // The one structural difference from the GitHub logic drives most of this file: a GitLab
7
+ // project path NESTS. `group/sub/project` is a legal project, and the client folds it into
8
+ // `{ owner: 'group/sub', repo: 'project' }` (splitting at the LAST slash, the same way
9
+ // GitLab's own web URLs read), so an owner here is a multi-segment path where GitHub's is
10
+ // a single account name. Reusing the GitHub grammar would not merely be inconvenient: a
11
+ // subgroup issue would round-trip through it as garbage rather than as a refusal.
12
+ //
13
+ // GitLab issue descriptions are already Markdown, so there is no body-conversion step.
14
+ /**
15
+ * What the connect UI renders. GitLab issues ride the workspace's existing VCS connection
16
+ * (the `provider: 'gitlab'` row a PAT connect writes), so there are NO credential fields —
17
+ * the same credentialless shape GitHub Issues has, keyed on a different connection.
18
+ */
19
+ export const GITLAB_ISSUES_DESCRIPTOR = {
20
+ source: 'gitlab',
21
+ label: 'GitLab Issues',
22
+ icon: 'i-lucide-gitlab',
23
+ credentialFields: [],
24
+ refLabel: 'Issue URL or group/project#number',
25
+ refPlaceholder: 'acme/web#123 or https://gitlab.com/acme/web/-/issues/123',
26
+ searchable: true,
27
+ };
28
+ /** The canonical `group/sub/project#iid` external id for an issue's parts. */
29
+ export function gitlabIssueExternalId(id) {
30
+ return `${id.owner}/${id.repo}#${id.number}`;
31
+ }
32
+ // A single path segment of a GitLab project path: letters, digits, '.', '_' and '-'.
33
+ const SEG = '[A-Za-z0-9._-]+';
34
+ // A full project path: at least two segments (a namespace and the project), and possibly
35
+ // more, because GitLab nests subgroups. Non-greedy up to the LAST slash so the split below
36
+ // matches how the client folds a web URL.
37
+ const PROJECT_PATH = `${SEG}(?:/${SEG})+`;
38
+ /**
39
+ * Split a project path at its LAST slash, which is the same fold the GitLab client applies
40
+ * to a `web_url`: everything before is the namespace (`group/sub`), the final segment is the
41
+ * project. Returns null for a path with no slash, i.e. one that names no project.
42
+ */
43
+ function splitProjectPath(path) {
44
+ const idx = path.lastIndexOf('/');
45
+ if (idx <= 0 || idx === path.length - 1)
46
+ return null;
47
+ return { owner: path.slice(0, idx), repo: path.slice(idx + 1) };
48
+ }
49
+ /**
50
+ * Resolve a GitLab issue reference from raw user input into the canonical
51
+ * `group/sub/project#iid` external id. Accepts:
52
+ * - a full issue URL: `https://gitlab.example.com/acme/sub/web/-/issues/123`
53
+ * - the `acme/sub/web/-/issues/123` path form
54
+ * - the shorthand `acme/sub/web#123`
55
+ * Returns null when nothing parses. Path segments are kept verbatim (a GitLab path is
56
+ * case-sensitive in a way GitHub's is not); only surrounding whitespace is trimmed.
57
+ *
58
+ * The URL form matches the `/-/issues/` separator GitLab puts between a project path and a
59
+ * resource, which is also what makes an arbitrarily deep project path unambiguous: without
60
+ * it there would be no way to tell where the path ends and `issues/123` begins.
61
+ */
62
+ export function parseGitLabIssueRef(input) {
63
+ const trimmed = input.trim();
64
+ const url = trimmed.match(new RegExp(`^https?://[^/]+/(${PROJECT_PATH})/-/issues/(\\d+)`));
65
+ if (url)
66
+ return `${url[1]}#${url[2]}`;
67
+ const path = trimmed.match(new RegExp(`^(${PROJECT_PATH})/-/issues/(\\d+)$`));
68
+ if (path)
69
+ return `${path[1]}#${path[2]}`;
70
+ const short = trimmed.match(new RegExp(`^(${PROJECT_PATH})#(\\d+)$`));
71
+ if (short)
72
+ return `${short[1]}#${short[2]}`;
73
+ return null;
74
+ }
75
+ /**
76
+ * Split a stored `group/sub/project#iid` external id back into its parts. Returns null if
77
+ * the id is malformed (defensive — ids are produced by {@link parseGitLabIssueRef}, but a
78
+ * stale or hand-edited row should not throw).
79
+ */
80
+ export function parseGitLabIssueExternalId(externalId) {
81
+ const m = externalId.match(new RegExp(`^(${PROJECT_PATH})#(\\d+)$`));
82
+ if (!m)
83
+ return null;
84
+ const parts = splitProjectPath(m[1]);
85
+ if (!parts)
86
+ return null;
87
+ return { owner: parts.owner, repo: parts.repo, number: Number(m[2]) };
88
+ }
89
+ /**
90
+ * Build an issue's web URL from the deployment's GitLab web base, for the one case where the
91
+ * API answered without a `web_url` of its own.
92
+ *
93
+ * There is deliberately no constant fallback: a self-managed GitLab lives at the
94
+ * deployment's own host, so `gitlab.com` would not be a guess that is usually right, it
95
+ * would be a link to a stranger's instance. `undefined` in, `''` out — an empty URL renders
96
+ * as no link, where a wrong one renders as a link to the wrong issue.
97
+ */
98
+ export function gitlabIssueUrl(id, webBaseUrl) {
99
+ const base = webBaseUrl?.replace(/\/+$/, '');
100
+ if (!base)
101
+ return '';
102
+ return `${base}/${id.owner}/${id.repo}/-/issues/${id.number}`;
103
+ }
104
+ /**
105
+ * Derive the GitLab WEB base from the configured API base, which is the same host with the
106
+ * REST prefix on the end (`https://gitlab.example.com/api/v4`). Returns undefined for an
107
+ * absent/unparseable value rather than a guess, so {@link gitlabIssueUrl} withholds its link.
108
+ */
109
+ export function gitlabWebBaseFromApiBase(apiBase) {
110
+ const trimmed = apiBase?.trim().replace(/\/+$/, '');
111
+ if (!trimmed)
112
+ return undefined;
113
+ const stripped = trimmed.replace(/\/api\/v\d+$/, '');
114
+ return stripped || undefined;
115
+ }
116
+ /**
117
+ * The provider's `TaskRepoScopeRules` matcher: whether a STORED external id belongs to the
118
+ * scoped project.
119
+ *
120
+ * Case-SENSITIVE, where the GitHub twin is not, and that asymmetry is the whole reason the
121
+ * comparison belongs to the source rather than to a shared helper. A GitLab project path is the
122
+ * `path_with_namespace` both sides are built from (the id is parsed out of a `web_url`, the scope
123
+ * comes from the repo projection's own fold of the same field), so they already agree; folding
124
+ * case on top would let `Acme/web` and `acme/web`, which GitLab serves as two different projects,
125
+ * answer for each other.
126
+ *
127
+ * An id that does not parse is out of scope, same reading as the GitHub matcher's.
128
+ */
129
+ export function gitlabIssueInRepoScope(externalId, scope) {
130
+ const id = parseGitLabIssueExternalId(externalId);
131
+ return !!id && id.owner === scope.owner && id.repo === scope.repo;
132
+ }
133
+ /**
134
+ * Build the project-scoped issue search for the picker's free-text box. The scope is the
135
+ * repository the searching service frame is linked to, and it is an ARGUMENT of the request
136
+ * rather than text in the query, so there is no unscoped spelling of this search to reach by
137
+ * accident — see the kernel port's note on why GitLab in particular cannot express the scope
138
+ * as a qualifier.
139
+ *
140
+ * Both states are searched, not just open ones: a picker attaches an issue for CONTEXT, and
141
+ * a closed issue is routinely the one a follow-up task points at. (The intake/hunt reads,
142
+ * which pick work to START, are the ones that set `openOnly`.)
143
+ */
144
+ export function buildGitLabIssueSearchQuery(query, limit) {
145
+ const text = query.trim();
146
+ return { limit, ...(text ? { text } : {}) };
147
+ }
148
+ /**
149
+ * Resolve raw search input that names ONE specific issue in the SCOPED project (rather than
150
+ * free text) into its canonical external id, so the caller can fetch it and offer it as the
151
+ * exact match. Two forms, mirroring the GitHub logic's:
152
+ * 1. An explicit reference (issue URL, `/-/issues/` path, or `group/project#n` shorthand),
153
+ * accepted only when it names the SCOPED project.
154
+ * 2. A bare issue number, resolved against `scope` — the only way to know which project a
155
+ * lone number belongs to.
156
+ * Returns null when the input is neither (treat it as free-text search).
157
+ *
158
+ * A reference naming a DIFFERENT project resolves to null on purpose: a search returns the
159
+ * service's own project and nothing else, so a stray paste can never be dressed up as a hit
160
+ * the search found. Linking that issue is still supported through the picker's explicit
161
+ * attach-by-reference row, which imports the ref directly.
162
+ *
163
+ * Unlike the GitHub twin the comparison is case-SENSITIVE, and the id is echoed from the
164
+ * scope for the same reason that one is: GitLab project paths are lowercase by construction
165
+ * but their display forms are not, and an external id is stored verbatim, so normalising
166
+ * here is what stops one issue becoming two rows.
167
+ */
168
+ export function detectExactGitLabIssueRef(query, scope) {
169
+ const trimmed = query.trim();
170
+ const ref = parseGitLabIssueRef(trimmed);
171
+ if (ref) {
172
+ const id = parseGitLabIssueExternalId(ref);
173
+ if (!id || id.owner !== scope.owner || id.repo !== scope.repo)
174
+ return null;
175
+ return gitlabIssueExternalId({ owner: scope.owner, repo: scope.repo, number: id.number });
176
+ }
177
+ if (/^\d+$/.test(trimmed)) {
178
+ return gitlabIssueExternalId({
179
+ owner: scope.owner,
180
+ repo: scope.repo,
181
+ number: Number(trimmed),
182
+ });
183
+ }
184
+ return null;
185
+ }
186
+ //# sourceMappingURL=gitlab-issues.logic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitlab-issues.logic.js","sourceRoot":"","sources":["../../../src/modules/tasks/gitlab-issues.logic.ts"],"names":[],"mappings":"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"}
@@ -10,15 +10,21 @@ export declare class MapTaskSourceRegistry extends MapSourceRegistry<TaskSourceK
10
10
  /** A short plain-text excerpt of an issue: its summary + the start of its description. */
11
11
  export declare function buildTaskExcerpt(content: TaskContent | TaskRecord, max?: number): string;
12
12
  /**
13
- * Whether an imported task belongs to a repo scope. Only GitHub issues carry a
14
- * repo (their `owner/repo#number` external id), so a repo-less source (Jira,
15
- * Linear) always passes the scope narrows the GitHub view without hiding
16
- * trackers that have no repo notion. Matching is case-insensitive (GitHub
17
- * owner/repo names are), mirroring the `repo:owner/name` search qualifier. A
18
- * GitHub id that doesn't parse (a stale/hand-edited row) is treated as
19
- * out-of-scope rather than leaking into every repo's list.
13
+ * Whether an imported task belongs to a repo scope, asked of the source that minted its
14
+ * external id: a repo-backed provider (GitHub Issues, GitLab Issues) declares `repoScope` and
15
+ * owns the comparison, because the id GRAMMAR and its case rules are the source's own.
16
+ *
17
+ * A source with no `repoScope` passes unfiltered, and that covers two different situations that
18
+ * happen to want the same answer. A repo-LESS source (Jira, Linear) has no repository to be
19
+ * narrowed to, so the scope simply does not apply to its rows. An UNREGISTERED source (a row
20
+ * left behind by a provider this deployment no longer wires) has no rule available to judge it
21
+ * by, and dropping a row a scope cannot evaluate would silently shrink the list rather than
22
+ * narrow it: the reader would read the absence as "this service has no such issue".
23
+ *
24
+ * Passing the provider rather than looking it up here keeps this pure, and lets the caller
25
+ * resolve each source ONCE for a whole list instead of per row.
20
26
  */
21
- export declare function taskInRepoScope(record: Pick<TaskRecord, 'source' | 'externalId'>, scope: TaskSearchRepoScope): boolean;
27
+ export declare function taskInRepoScope(record: Pick<TaskRecord, 'source' | 'externalId'>, scope: TaskSearchRepoScope, provider: TaskSourceProvider | undefined): boolean;
22
28
  /**
23
29
  * Read a numeric HTTP status off a thrown error, if it carries one. Both the
24
30
  * GitHub (`GitHubApiError`) and Jira (`JiraApiError`) clients expose a `status`
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/tasks/tasks.logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC9E,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAgC,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAGrF,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAQvD,4EAA4E;AAC5E,qBAAa,qBACX,SAAQ,iBAAiB,CAAC,cAAc,EAAE,kBAAkB,CAC5D,YAAW,kBAAkB;CAAG;AAElC,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,EAAE,GAAG,SAAM,GAAG,MAAM,CAIrF;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,YAAY,CAAC,EACjD,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAQT;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAMxD"}
1
+ {"version":3,"file":"tasks.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/tasks/tasks.logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC9E,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAgC,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAErF,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAQvD,4EAA4E;AAC5E,qBAAa,qBACX,SAAQ,iBAAiB,CAAC,cAAc,EAAE,kBAAkB,CAC5D,YAAW,kBAAkB;CAAG;AAElC,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,EAAE,GAAG,SAAM,GAAG,MAAM,CAIrF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,YAAY,CAAC,EACjD,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,EAAE,kBAAkB,GAAG,SAAS,GACvC,OAAO,CAIT;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAMxD"}
@@ -1,5 +1,4 @@
1
1
  import { markdownToText, buildExcerpt, MapSourceRegistry } from '@cat-factory/kernel';
2
- import { parseGitHubIssueExternalId } from './github-issues.logic.js';
3
2
  export { renderTaskContext } from '@cat-factory/kernel';
4
3
  // Source-agnostic helpers shared by every task source: a trivial provider
5
4
  // registry, deriving a plain-text excerpt from an issue, and rendering an issue
@@ -16,22 +15,25 @@ export function buildTaskExcerpt(content, max = 280) {
16
15
  return buildExcerpt(lead, max);
17
16
  }
18
17
  /**
19
- * Whether an imported task belongs to a repo scope. Only GitHub issues carry a
20
- * repo (their `owner/repo#number` external id), so a repo-less source (Jira,
21
- * Linear) always passes the scope narrows the GitHub view without hiding
22
- * trackers that have no repo notion. Matching is case-insensitive (GitHub
23
- * owner/repo names are), mirroring the `repo:owner/name` search qualifier. A
24
- * GitHub id that doesn't parse (a stale/hand-edited row) is treated as
25
- * out-of-scope rather than leaking into every repo's list.
18
+ * Whether an imported task belongs to a repo scope, asked of the source that minted its
19
+ * external id: a repo-backed provider (GitHub Issues, GitLab Issues) declares `repoScope` and
20
+ * owns the comparison, because the id GRAMMAR and its case rules are the source's own.
21
+ *
22
+ * A source with no `repoScope` passes unfiltered, and that covers two different situations that
23
+ * happen to want the same answer. A repo-LESS source (Jira, Linear) has no repository to be
24
+ * narrowed to, so the scope simply does not apply to its rows. An UNREGISTERED source (a row
25
+ * left behind by a provider this deployment no longer wires) has no rule available to judge it
26
+ * by, and dropping a row a scope cannot evaluate would silently shrink the list rather than
27
+ * narrow it: the reader would read the absence as "this service has no such issue".
28
+ *
29
+ * Passing the provider rather than looking it up here keeps this pure, and lets the caller
30
+ * resolve each source ONCE for a whole list instead of per row.
26
31
  */
27
- export function taskInRepoScope(record, scope) {
28
- if (record.source !== 'github')
32
+ export function taskInRepoScope(record, scope, provider) {
33
+ const rules = provider?.repoScope;
34
+ if (!rules)
29
35
  return true;
30
- const parts = parseGitHubIssueExternalId(record.externalId);
31
- if (!parts)
32
- return false;
33
- return (parts.owner.toLowerCase() === scope.owner.toLowerCase() &&
34
- parts.repo.toLowerCase() === scope.repo.toLowerCase());
36
+ return rules.matches(record.externalId, scope);
35
37
  }
36
38
  /**
37
39
  * Read a numeric HTTP status off a thrown error, if it carries one. Both the
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.logic.js","sourceRoot":"","sources":["../../../src/modules/tasks/tasks.logic.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAGrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAEvD,0EAA0E;AAC1E,gFAAgF;AAChF,kFAAkF;AAClF,yEAAyE;AACzE,sEAAsE;AAEtE,4EAA4E;AAC5E,MAAM,OAAO,qBACX,SAAQ,iBAAqD;CAC7B;AAElC,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,OAAiC,EAAE,GAAG,GAAG,GAAG;IAC3E,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IACvD,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,MAAM,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAA;IAC9E,OAAO,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AAChC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAiD,EACjD,KAA0B;IAE1B,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC3C,MAAM,KAAK,GAAG,0BAA0B,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,OAAO,CACL,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE;QACvD,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CACtD,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;QACtD,MAAM,MAAM,GAAI,GAA2B,CAAC,MAAM,CAAA;QAClD,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAA;IAC/C,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
1
+ {"version":3,"file":"tasks.logic.js","sourceRoot":"","sources":["../../../src/modules/tasks/tasks.logic.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAGrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAEvD,0EAA0E;AAC1E,gFAAgF;AAChF,kFAAkF;AAClF,yEAAyE;AACzE,sEAAsE;AAEtE,4EAA4E;AAC5E,MAAM,OAAO,qBACX,SAAQ,iBAAqD;CAC7B;AAElC,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,OAAiC,EAAE,GAAG,GAAG,GAAG;IAC3E,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IACvD,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,MAAM,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAA;IAC9E,OAAO,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AAChC,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAiD,EACjD,KAA0B,EAC1B,QAAwC;IAExC,MAAM,KAAK,GAAG,QAAQ,EAAE,SAAS,CAAA;IACjC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,CAAA;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;QACtD,MAAM,MAAM,GAAI,GAA2B,CAAC,MAAM,CAAA;QAClD,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAA;IAC/C,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/integrations",
3
- "version": "0.141.2",
3
+ "version": "0.142.0",
4
4
  "description": "External-system integration domain logic for the Agent Architecture Board (GitHub, documents, tasks, environments, runners).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,15 +27,15 @@
27
27
  "ai": "^7.0.51",
28
28
  "p-map": "^7.0.6",
29
29
  "yaml": "^2.9.0",
30
- "@cat-factory/kernel": "0.262.2",
31
- "@cat-factory/contracts": "0.264.0"
30
+ "@cat-factory/contracts": "0.265.0",
31
+ "@cat-factory/kernel": "0.263.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "typescript": "7.0.2",
35
35
  "undici": "^8.10.0",
36
36
  "valibot": "^1.4.2",
37
37
  "vitest": "^4.1.10",
38
- "@cat-factory/caching": "0.18.3"
38
+ "@cat-factory/caching": "0.18.4"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc -b tsconfig.build.json",