@availsync/node 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +89 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Availsync
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# @availsync/node
|
|
2
|
+
|
|
3
|
+
Node SDK for Availsync coding-agent work guardrails.
|
|
4
|
+
|
|
5
|
+
Use it before Codex, Claude, Cursor, OpenClaw, cron jobs, deploy scripts, or other automations touch a protected repo or project.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @availsync/node@alpha
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Guard a coding-agent run
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createAvailsyncClient } from '@availsync/node';
|
|
17
|
+
|
|
18
|
+
const availsync = createAvailsyncClient({
|
|
19
|
+
apiKey: process.env.AVAILSYNC_API_KEY!,
|
|
20
|
+
agentId: process.env.AVAILSYNC_AGENT_ID!,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const result = await availsync.work.withClaim(
|
|
24
|
+
'repo:owner/repo',
|
|
25
|
+
{ reason: 'scheduled Codex run', durationMinutes: 45 },
|
|
26
|
+
async ({ claim, shadow }) => {
|
|
27
|
+
if (shadow.wouldHaveBlocked) {
|
|
28
|
+
console.warn('Observe-only: Availsync would have skipped this run.');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await doWork();
|
|
32
|
+
|
|
33
|
+
// Optional for long-running work. In observe mode there is no active claim.
|
|
34
|
+
if (claim) await claim.extend({ durationMinutes: 45 });
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (result.action === 'skip_run') {
|
|
39
|
+
console.log(result.reason);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Manual flow
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const started = await availsync.work.start({
|
|
47
|
+
resource: 'repo:owner/repo',
|
|
48
|
+
reason: 'deploy automation',
|
|
49
|
+
durationMinutes: 45,
|
|
50
|
+
idempotencyKey: `deploy-${process.env.GITHUB_RUN_ID}`,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (started.action === 'skip_run') {
|
|
54
|
+
console.log(started.reason);
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (started.shadow_mode && started.would_have_blocked) {
|
|
59
|
+
console.warn('Observe-only: Availsync would have skipped this run.');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
await doWork();
|
|
64
|
+
if (started.claim) {
|
|
65
|
+
await availsync.work.extend(started.claim.id, { durationMinutes: 45 });
|
|
66
|
+
}
|
|
67
|
+
} finally {
|
|
68
|
+
if (started.claim) {
|
|
69
|
+
await availsync.work.finish(started.claim.id, { outcome: 'finished' });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Observe-only pilots
|
|
75
|
+
|
|
76
|
+
Set an agent to Observe in the dashboard when you want Availsync to report what it would have blocked without stopping the automation. Observe mode returns `action: "proceed"` and `claim: null`; no lease is created, so `extend` and `finish` are no-ops you should skip.
|
|
77
|
+
|
|
78
|
+
## Resource scope
|
|
79
|
+
|
|
80
|
+
- `repo:owner/repo` is the safe default. Only one active agent can work in the repo.
|
|
81
|
+
- `project:homepage` allows parallel work, but only use it for independent work streams.
|
|
82
|
+
- Availsync does not yet infer dependencies between different resources.
|
|
83
|
+
|
|
84
|
+
## Local development
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm run build:sdk
|
|
88
|
+
npm run test:sdk
|
|
89
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export type ResourceType = 'repo' | 'project';
|
|
2
|
+
export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
3
|
+
export type AvailsyncClientOptions = {
|
|
4
|
+
apiKey: string;
|
|
5
|
+
agentId: string;
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
fetch?: FetchLike;
|
|
8
|
+
};
|
|
9
|
+
export type WorkStartOptions = {
|
|
10
|
+
resource: string;
|
|
11
|
+
durationMinutes?: number;
|
|
12
|
+
reason?: string;
|
|
13
|
+
idempotencyKey?: string;
|
|
14
|
+
metadata?: Record<string, unknown>;
|
|
15
|
+
};
|
|
16
|
+
export type WorkExtendOptions = {
|
|
17
|
+
durationMinutes?: number;
|
|
18
|
+
};
|
|
19
|
+
export type WorkFinishOptions = {
|
|
20
|
+
outcome?: string;
|
|
21
|
+
reason?: string;
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
export type ParsedResource = {
|
|
25
|
+
resourceType: ResourceType;
|
|
26
|
+
resourceKey: string;
|
|
27
|
+
};
|
|
28
|
+
export type WorkClaim = {
|
|
29
|
+
id: string;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
};
|
|
32
|
+
export type ShadowDecision = {
|
|
33
|
+
action: 'skip_run';
|
|
34
|
+
reason?: string;
|
|
35
|
+
retry_after?: string | null;
|
|
36
|
+
blocking_claim?: Record<string, unknown> | null;
|
|
37
|
+
rule_applied?: string | null;
|
|
38
|
+
winning_claim_id?: string | null;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
};
|
|
41
|
+
export type WorkRunStartProceed = {
|
|
42
|
+
action: 'proceed';
|
|
43
|
+
claim: WorkClaim | null;
|
|
44
|
+
resource?: Record<string, unknown>;
|
|
45
|
+
reason?: string;
|
|
46
|
+
retry_after?: null;
|
|
47
|
+
blocking_claim?: null;
|
|
48
|
+
shadow_mode?: boolean;
|
|
49
|
+
would_have_blocked?: boolean;
|
|
50
|
+
shadow_decision?: ShadowDecision | null;
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
};
|
|
53
|
+
export type WorkRunStartSkip = {
|
|
54
|
+
action: 'skip_run';
|
|
55
|
+
claim?: null;
|
|
56
|
+
resource?: Record<string, unknown>;
|
|
57
|
+
reason: string;
|
|
58
|
+
retry_after?: string | null;
|
|
59
|
+
blocking_claim?: Record<string, unknown> | null;
|
|
60
|
+
[key: string]: unknown;
|
|
61
|
+
};
|
|
62
|
+
export type WorkRunStartResult = WorkRunStartProceed | WorkRunStartSkip;
|
|
63
|
+
export type WorkRunExtendResult = {
|
|
64
|
+
action: 'extended';
|
|
65
|
+
claim: WorkClaim;
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
};
|
|
68
|
+
export type WorkRunFinishResult = {
|
|
69
|
+
action: 'finished' | 'already_finished';
|
|
70
|
+
claim: WorkClaim;
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
};
|
|
73
|
+
export type WithClaimSkipResult = {
|
|
74
|
+
action: 'skip_run';
|
|
75
|
+
reason: string;
|
|
76
|
+
retryAfter: string | null;
|
|
77
|
+
blockingClaim: Record<string, unknown> | null;
|
|
78
|
+
};
|
|
79
|
+
export type WithClaimProceedResult<T> = {
|
|
80
|
+
action: 'proceed';
|
|
81
|
+
claim: GuardedWorkClaim | null;
|
|
82
|
+
result: T;
|
|
83
|
+
shadowMode: boolean;
|
|
84
|
+
wouldHaveBlocked: boolean;
|
|
85
|
+
shadowDecision: ShadowDecision | null;
|
|
86
|
+
};
|
|
87
|
+
export type GuardedWorkClaim = WorkClaim & {
|
|
88
|
+
extend(options?: WorkExtendOptions): Promise<WorkRunExtendResult>;
|
|
89
|
+
finish(options?: WorkFinishOptions): Promise<WorkRunFinishResult>;
|
|
90
|
+
};
|
|
91
|
+
export type WithClaimContext = {
|
|
92
|
+
claim: GuardedWorkClaim | null;
|
|
93
|
+
shadow: {
|
|
94
|
+
mode: boolean;
|
|
95
|
+
wouldHaveBlocked: boolean;
|
|
96
|
+
decision: ShadowDecision | null;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
export declare class AvailsyncError extends Error {
|
|
100
|
+
readonly code: string;
|
|
101
|
+
readonly status: number | null;
|
|
102
|
+
readonly details?: unknown;
|
|
103
|
+
constructor(input: {
|
|
104
|
+
code: string;
|
|
105
|
+
message: string;
|
|
106
|
+
status?: number | null;
|
|
107
|
+
details?: unknown;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
export declare function parseResource(resource: string): ParsedResource;
|
|
111
|
+
export declare function createAvailsyncClient(options: AvailsyncClientOptions): {
|
|
112
|
+
work: {
|
|
113
|
+
start: (input: WorkStartOptions) => Promise<WorkRunStartResult>;
|
|
114
|
+
extend: (claimId: string, input?: WorkExtendOptions) => Promise<WorkRunExtendResult>;
|
|
115
|
+
finish: (claimId: string, input?: WorkFinishOptions) => Promise<WorkRunFinishResult>;
|
|
116
|
+
withClaim: <T>(resource: string, input: Omit<WorkStartOptions, "resource">, fn: (context: WithClaimContext) => Promise<T> | T) => Promise<WithClaimSkipResult | WithClaimProceedResult<T>>;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
export class AvailsyncError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
status;
|
|
4
|
+
details;
|
|
5
|
+
constructor(input) {
|
|
6
|
+
super(input.message);
|
|
7
|
+
this.name = 'AvailsyncError';
|
|
8
|
+
this.code = input.code;
|
|
9
|
+
this.status = input.status ?? null;
|
|
10
|
+
this.details = input.details;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function parseResource(resource) {
|
|
14
|
+
const separator = resource.indexOf(':');
|
|
15
|
+
if (separator <= 0 || separator === resource.length - 1) {
|
|
16
|
+
throw new AvailsyncError({
|
|
17
|
+
code: 'invalid_resource',
|
|
18
|
+
message: 'Resource must use "repo:owner/name" or "project:key" format.',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const resourceType = resource.slice(0, separator);
|
|
22
|
+
const resourceKey = resource.slice(separator + 1);
|
|
23
|
+
if ((resourceType !== 'repo' && resourceType !== 'project') || resourceKey.trim() !== resourceKey || !resourceKey) {
|
|
24
|
+
throw new AvailsyncError({
|
|
25
|
+
code: 'invalid_resource',
|
|
26
|
+
message: 'Resource must use "repo:owner/name" or "project:key" format.',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return { resourceType, resourceKey };
|
|
30
|
+
}
|
|
31
|
+
export function createAvailsyncClient(options) {
|
|
32
|
+
if (!options.apiKey) {
|
|
33
|
+
throw new AvailsyncError({ code: 'missing_api_key', message: 'apiKey is required.' });
|
|
34
|
+
}
|
|
35
|
+
if (!options.agentId) {
|
|
36
|
+
throw new AvailsyncError({ code: 'missing_agent_id', message: 'agentId is required.' });
|
|
37
|
+
}
|
|
38
|
+
const baseUrl = (options.baseUrl ?? 'https://availsync.dev').replace(/\/+$/, '');
|
|
39
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
40
|
+
if (!fetchImpl) {
|
|
41
|
+
throw new AvailsyncError({
|
|
42
|
+
code: 'fetch_unavailable',
|
|
43
|
+
message: 'Global fetch is unavailable. Use Node 18+ or pass a fetch implementation.',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async function request(path, init) {
|
|
47
|
+
let response;
|
|
48
|
+
try {
|
|
49
|
+
response = await fetchImpl(`${baseUrl}${path}`, init);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
throw new AvailsyncError({
|
|
53
|
+
code: 'network_error',
|
|
54
|
+
message: 'Network error calling Availsync.',
|
|
55
|
+
details: error instanceof Error ? { message: error.message } : undefined,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const body = await parseJson(response);
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const errorBody = asRecord(body);
|
|
61
|
+
throw new AvailsyncError({
|
|
62
|
+
code: stringFrom(errorBody.error) ?? `http_${response.status}`,
|
|
63
|
+
message: stringFrom(errorBody.message) ?? 'Availsync API request failed.',
|
|
64
|
+
status: response.status,
|
|
65
|
+
details: body,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return body;
|
|
69
|
+
}
|
|
70
|
+
function headers(idempotencyKey) {
|
|
71
|
+
return {
|
|
72
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function start(input) {
|
|
78
|
+
const resource = parseResource(input.resource);
|
|
79
|
+
return request('/v1/work/run/start', {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: headers(input.idempotencyKey),
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
agent_id: options.agentId,
|
|
84
|
+
resource_type: resource.resourceType,
|
|
85
|
+
resource_key: resource.resourceKey,
|
|
86
|
+
duration_minutes: input.durationMinutes,
|
|
87
|
+
reason: input.reason,
|
|
88
|
+
metadata: input.metadata,
|
|
89
|
+
client_name: '@availsync/node',
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async function extend(claimId, input = {}) {
|
|
94
|
+
return request('/v1/work/run/extend', {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
headers: headers(),
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
claim_id: claimId,
|
|
99
|
+
duration_minutes: input.durationMinutes,
|
|
100
|
+
client_name: '@availsync/node',
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async function finish(claimId, input = {}) {
|
|
105
|
+
return request('/v1/work/run/finish', {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: headers(),
|
|
108
|
+
body: JSON.stringify({
|
|
109
|
+
claim_id: claimId,
|
|
110
|
+
outcome: input.outcome,
|
|
111
|
+
reason: input.reason,
|
|
112
|
+
metadata: input.metadata,
|
|
113
|
+
client_name: '@availsync/node',
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function guardedClaim(claim) {
|
|
118
|
+
return {
|
|
119
|
+
...claim,
|
|
120
|
+
extend: (input) => extend(claim.id, input),
|
|
121
|
+
finish: (input) => finish(claim.id, input),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async function withClaim(resource, input, fn) {
|
|
125
|
+
const startResult = await start({ ...input, resource });
|
|
126
|
+
if (startResult.action === 'skip_run') {
|
|
127
|
+
return {
|
|
128
|
+
action: 'skip_run',
|
|
129
|
+
reason: startResult.reason,
|
|
130
|
+
retryAfter: startResult.retry_after ?? null,
|
|
131
|
+
blockingClaim: startResult.blocking_claim ?? null,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const shadowMode = Boolean(startResult.shadow_mode);
|
|
135
|
+
const wouldHaveBlocked = Boolean(startResult.would_have_blocked);
|
|
136
|
+
const shadowDecision = startResult.shadow_decision ?? null;
|
|
137
|
+
if (!startResult.claim) {
|
|
138
|
+
const result = await fn({
|
|
139
|
+
claim: null,
|
|
140
|
+
shadow: { mode: shadowMode, wouldHaveBlocked, decision: shadowDecision },
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
action: 'proceed',
|
|
144
|
+
claim: null,
|
|
145
|
+
result,
|
|
146
|
+
shadowMode,
|
|
147
|
+
wouldHaveBlocked,
|
|
148
|
+
shadowDecision,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const claim = guardedClaim(startResult.claim);
|
|
152
|
+
try {
|
|
153
|
+
const result = await fn({
|
|
154
|
+
claim,
|
|
155
|
+
shadow: { mode: shadowMode, wouldHaveBlocked, decision: shadowDecision },
|
|
156
|
+
});
|
|
157
|
+
await finish(claim.id, { outcome: 'finished' });
|
|
158
|
+
return { action: 'proceed', claim, result, shadowMode, wouldHaveBlocked, shadowDecision };
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
try {
|
|
162
|
+
await finish(claim.id, { outcome: 'error' });
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Preserve the original agent error; finish failures are visible in Availsync Activity.
|
|
166
|
+
}
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
work: {
|
|
172
|
+
start,
|
|
173
|
+
extend,
|
|
174
|
+
finish,
|
|
175
|
+
withClaim,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async function parseJson(response) {
|
|
180
|
+
const text = await response.text();
|
|
181
|
+
if (!text)
|
|
182
|
+
return {};
|
|
183
|
+
try {
|
|
184
|
+
return JSON.parse(text);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
throw new AvailsyncError({
|
|
188
|
+
code: 'invalid_json',
|
|
189
|
+
message: 'Availsync API returned invalid JSON.',
|
|
190
|
+
status: response.status,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function asRecord(value) {
|
|
195
|
+
return value && typeof value === 'object' ? value : {};
|
|
196
|
+
}
|
|
197
|
+
function stringFrom(value) {
|
|
198
|
+
return typeof value === 'string' ? value : null;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@availsync/node",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Node SDK for Availsync coding-agent work guardrails.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/arkilstsko/availsync.git",
|
|
24
|
+
"directory": "packages/node"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://availsync.dev",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/arkilstsko/availsync/issues"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"availsync",
|
|
32
|
+
"ai-agents",
|
|
33
|
+
"coding-agents",
|
|
34
|
+
"guardrails",
|
|
35
|
+
"mcp",
|
|
36
|
+
"automation",
|
|
37
|
+
"repo-locks"
|
|
38
|
+
],
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public",
|
|
41
|
+
"tag": "alpha"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc -p tsconfig.json",
|
|
48
|
+
"prepack": "npm run build",
|
|
49
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
50
|
+
"test": "jest --config jest.config.cjs --runInBand"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {},
|
|
53
|
+
"devDependencies": {}
|
|
54
|
+
}
|