@occam-scaly/scaly-cli 0.1.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/README.md +39 -0
- package/bin/scaly-public.js +64 -0
- package/bin/scaly.js +3083 -0
- package/lib/scaly-api.js +331 -0
- package/lib/scaly-apply.js +85 -0
- package/lib/scaly-auth.js +411 -0
- package/lib/scaly-deploy.js +137 -0
- package/lib/scaly-logs.js +110 -0
- package/lib/scaly-plan.js +392 -0
- package/lib/scaly-project.js +303 -0
- package/lib/scaly-secrets.js +91 -0
- package/lib/stable-json.js +45 -0
- package/package.json +27 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const api = require('./scaly-api');
|
|
4
|
+
|
|
5
|
+
const LIST_BUILD_SECRETS = `
|
|
6
|
+
query ListAppBuildSecrets($appId: String!) {
|
|
7
|
+
listAppBuildSecrets(appId: $appId) {
|
|
8
|
+
id
|
|
9
|
+
name
|
|
10
|
+
secretArn
|
|
11
|
+
createdAt
|
|
12
|
+
updatedAt
|
|
13
|
+
rotatedAt
|
|
14
|
+
createdByName
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const CREATE_BUILD_SECRET = `
|
|
20
|
+
mutation CreateAppBuildSecret($input: CreateAppBuildSecretInput!) {
|
|
21
|
+
createAppBuildSecret(input: $input) {
|
|
22
|
+
id
|
|
23
|
+
name
|
|
24
|
+
secretArn
|
|
25
|
+
createdAt
|
|
26
|
+
updatedAt
|
|
27
|
+
rotatedAt
|
|
28
|
+
createdByName
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const UPDATE_BUILD_SECRET = `
|
|
34
|
+
mutation UpdateAppBuildSecret($input: UpdateAppBuildSecretInput!) {
|
|
35
|
+
updateAppBuildSecret(input: $input) {
|
|
36
|
+
id
|
|
37
|
+
name
|
|
38
|
+
secretArn
|
|
39
|
+
createdAt
|
|
40
|
+
updatedAt
|
|
41
|
+
rotatedAt
|
|
42
|
+
createdByName
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const DELETE_BUILD_SECRET = `
|
|
48
|
+
mutation DeleteAppBuildSecret($input: DeleteAppBuildSecretInput!) {
|
|
49
|
+
deleteAppBuildSecret(input: $input)
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
async function listBuildSecrets(appId) {
|
|
54
|
+
const data = await api.graphqlRequest(LIST_BUILD_SECRETS, { appId });
|
|
55
|
+
return data?.listAppBuildSecrets || [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function setBuildSecret({ appId, name, value }) {
|
|
59
|
+
if (!appId) throw new Error('appId is required');
|
|
60
|
+
if (!name) throw new Error('name is required');
|
|
61
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
62
|
+
throw new Error('value is required');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const existing = await listBuildSecrets(appId);
|
|
66
|
+
const match = existing.find((s) => s.name === name) || null;
|
|
67
|
+
|
|
68
|
+
if (match) {
|
|
69
|
+
const data = await api.graphqlRequest(UPDATE_BUILD_SECRET, {
|
|
70
|
+
input: { id: match.id, value }
|
|
71
|
+
});
|
|
72
|
+
return { action: 'updated', secret: data?.updateAppBuildSecret };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const data = await api.graphqlRequest(CREATE_BUILD_SECRET, {
|
|
76
|
+
input: { appId, name, value }
|
|
77
|
+
});
|
|
78
|
+
return { action: 'created', secret: data?.createAppBuildSecret };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function deleteBuildSecret({ id }) {
|
|
82
|
+
if (!id) throw new Error('id is required');
|
|
83
|
+
const data = await api.graphqlRequest(DELETE_BUILD_SECRET, { input: { id } });
|
|
84
|
+
return Boolean(data?.deleteAppBuildSecret);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
listBuildSecrets,
|
|
89
|
+
setBuildSecret,
|
|
90
|
+
deleteBuildSecret
|
|
91
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
if (!value || typeof value !== 'object') return false;
|
|
7
|
+
const proto = Object.getPrototypeOf(value);
|
|
8
|
+
return proto === Object.prototype || proto === null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function stableNormalize(value) {
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
return value.map(stableNormalize);
|
|
14
|
+
}
|
|
15
|
+
if (isPlainObject(value)) {
|
|
16
|
+
const out = {};
|
|
17
|
+
const keys = Object.keys(value).sort();
|
|
18
|
+
for (const key of keys) {
|
|
19
|
+
const v = value[key];
|
|
20
|
+
if (typeof v === 'undefined') continue;
|
|
21
|
+
out[key] = stableNormalize(v);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function stableStringify(value) {
|
|
29
|
+
return JSON.stringify(stableNormalize(value));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function sha256Hex(input) {
|
|
33
|
+
return crypto.createHash('sha256').update(input).digest('hex');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function sha256Prefixed(input) {
|
|
37
|
+
return `sha256:${sha256Hex(input)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
stableNormalize,
|
|
42
|
+
stableStringify,
|
|
43
|
+
sha256Hex,
|
|
44
|
+
sha256Prefixed
|
|
45
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@occam-scaly/scaly-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaly CLI (auth + MCP helpers)",
|
|
5
|
+
"bin": {
|
|
6
|
+
"scaly": "./bin/scaly-public.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "node -c ./bin/scaly-public.js && node -c ./lib/scaly-auth.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"axios": "^1.7.9"
|
|
13
|
+
},
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18.0.0"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"bin",
|
|
23
|
+
"lib",
|
|
24
|
+
"README.md"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
|