ppcos 0.3.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 +9 -0
- package/README.md +246 -0
- package/bin/ppcos.js +91 -0
- package/lib/commands/init-all.js +247 -0
- package/lib/commands/init.js +229 -0
- package/lib/commands/login.js +85 -0
- package/lib/commands/logout.js +17 -0
- package/lib/commands/status.js +289 -0
- package/lib/commands/update.js +480 -0
- package/lib/commands/whoami.js +42 -0
- package/lib/utils/api-client.js +119 -0
- package/lib/utils/auth.js +117 -0
- package/lib/utils/checksum.js +61 -0
- package/lib/utils/fs-helpers.js +172 -0
- package/lib/utils/logger.js +51 -0
- package/lib/utils/manifest.js +212 -0
- package/lib/utils/skills-fetcher.js +50 -0
- package/lib/utils/validation.js +176 -0
- package/package.json +36 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validation utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Regex patterns per spec
|
|
6
|
+
const CLIENT_NAME_REGEX = /^[a-z][a-z0-9-]{0,49}$/;
|
|
7
|
+
const GOOGLE_ADS_ID_REGEX = /^\d{3}-\d{3}-\d{4}$/;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Validate a client name
|
|
11
|
+
* Rules:
|
|
12
|
+
* - Lowercase letters, numbers, hyphens only
|
|
13
|
+
* - Must start with letter
|
|
14
|
+
* - 1-50 characters
|
|
15
|
+
* - No spaces or special characters
|
|
16
|
+
*
|
|
17
|
+
* @param {string} name - Client name to validate
|
|
18
|
+
* @returns {{ valid: boolean, error?: string }} Validation result
|
|
19
|
+
*/
|
|
20
|
+
export function validateClientName(name) {
|
|
21
|
+
if (!name || typeof name !== 'string') {
|
|
22
|
+
return { valid: false, error: 'Client name is required' };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (name.length < 1) {
|
|
26
|
+
return { valid: false, error: 'Client name is required' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (name.length > 50) {
|
|
30
|
+
return { valid: false, error: 'Client name must be 1-50 characters' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!/^[a-z]/.test(name)) {
|
|
34
|
+
return { valid: false, error: 'Client name must start with a lowercase letter' };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!CLIENT_NAME_REGEX.test(name)) {
|
|
38
|
+
return {
|
|
39
|
+
valid: false,
|
|
40
|
+
error: 'Client name can only contain lowercase letters, numbers, and hyphens'
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { valid: true };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validate a Google Ads customer ID
|
|
49
|
+
* Format: XXX-XXX-XXXX (3 digits, 3 digits, 4 digits)
|
|
50
|
+
*
|
|
51
|
+
* @param {string} id - Customer ID to validate
|
|
52
|
+
* @returns {{ valid: boolean, error?: string }} Validation result
|
|
53
|
+
*/
|
|
54
|
+
export function validateGoogleAdsId(id) {
|
|
55
|
+
if (!id) {
|
|
56
|
+
return { valid: true }; // Optional field
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!GOOGLE_ADS_ID_REGEX.test(id)) {
|
|
60
|
+
return {
|
|
61
|
+
valid: false,
|
|
62
|
+
error: 'Google Ads Customer ID must be in format XXX-XXX-XXXX'
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { valid: true };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validate main-config.json structure
|
|
71
|
+
*
|
|
72
|
+
* @param {object} config - Config object to validate
|
|
73
|
+
* @returns {{ valid: boolean, errors: string[] }} Validation result
|
|
74
|
+
*/
|
|
75
|
+
export function validateConfig(config) {
|
|
76
|
+
const errors = [];
|
|
77
|
+
|
|
78
|
+
if (!config || typeof config !== 'object') {
|
|
79
|
+
return { valid: false, errors: ['Config must be an object'] };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Version check
|
|
83
|
+
if (config.version !== '1.0') {
|
|
84
|
+
errors.push('Unsupported config version (expected "1.0")');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Clients array check
|
|
88
|
+
if (!Array.isArray(config.clients)) {
|
|
89
|
+
errors.push('clients must be an array');
|
|
90
|
+
return { valid: false, errors };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Validate each client
|
|
94
|
+
const seenNames = new Set();
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < config.clients.length; i++) {
|
|
97
|
+
const client = config.clients[i];
|
|
98
|
+
const prefix = `clients[${i}]`;
|
|
99
|
+
|
|
100
|
+
if (!client || typeof client !== 'object') {
|
|
101
|
+
errors.push(`${prefix}: must be an object`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Name validation
|
|
106
|
+
const nameResult = validateClientName(client.name);
|
|
107
|
+
if (!nameResult.valid) {
|
|
108
|
+
errors.push(`${prefix}.name: ${nameResult.error}`);
|
|
109
|
+
} else if (seenNames.has(client.name)) {
|
|
110
|
+
errors.push(`${prefix}.name: duplicate client name "${client.name}"`);
|
|
111
|
+
} else {
|
|
112
|
+
seenNames.add(client.name);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Enabled field
|
|
116
|
+
if (typeof client.enabled !== 'boolean') {
|
|
117
|
+
errors.push(`${prefix}.enabled: must be a boolean`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Optional Google Ads ID
|
|
121
|
+
if (client.googleAdsCustomerId) {
|
|
122
|
+
const idResult = validateGoogleAdsId(client.googleAdsCustomerId);
|
|
123
|
+
if (!idResult.valid) {
|
|
124
|
+
errors.push(`${prefix}.googleAdsCustomerId: ${idResult.error}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Optional displayName
|
|
129
|
+
if (client.displayName !== undefined) {
|
|
130
|
+
if (typeof client.displayName !== 'string') {
|
|
131
|
+
errors.push(`${prefix}.displayName: must be a string`);
|
|
132
|
+
} else if (client.displayName.length > 100) {
|
|
133
|
+
errors.push(`${prefix}.displayName: must be 1-100 characters`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Optional website
|
|
138
|
+
if (client.website !== undefined && typeof client.website !== 'string') {
|
|
139
|
+
errors.push(`${prefix}.website: must be a string`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Optional settings validation
|
|
144
|
+
if (config.settings !== undefined) {
|
|
145
|
+
if (typeof config.settings !== 'object' || config.settings === null) {
|
|
146
|
+
errors.push('settings: must be an object');
|
|
147
|
+
} else if (config.settings.clientsDirectory !== undefined) {
|
|
148
|
+
if (typeof config.settings.clientsDirectory !== 'string') {
|
|
149
|
+
errors.push('settings.clientsDirectory: must be a string');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
valid: errors.length === 0,
|
|
156
|
+
errors
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Validate a manifest checksum format
|
|
162
|
+
* @param {string} checksum
|
|
163
|
+
* @returns {boolean}
|
|
164
|
+
*/
|
|
165
|
+
export function isValidChecksum(checksum) {
|
|
166
|
+
return typeof checksum === 'string' && /^sha256:[a-f0-9]{64}$/.test(checksum);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Validate a semver version string
|
|
171
|
+
* @param {string} version
|
|
172
|
+
* @returns {boolean}
|
|
173
|
+
*/
|
|
174
|
+
export function isValidVersion(version) {
|
|
175
|
+
return typeof version === 'string' && /^\d+\.\d+\.\d+$/.test(version);
|
|
176
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ppcos",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "CLI tool to manage Google Ads AI workflow skills and agents for Claude Code",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ppcos": "./bin/ppcos.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "vitest",
|
|
11
|
+
"test:run": "vitest run"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "Alfred Simon, Bob Meijer and Miles McNair",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/Pitcocy/ppcos.git"
|
|
18
|
+
},
|
|
19
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"commander": "^12.0.0",
|
|
22
|
+
"chalk": "^5.3.0",
|
|
23
|
+
"ora": "^8.0.0",
|
|
24
|
+
"unzipper": "^0.12.3"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"vitest": "^2.0.0"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"bin",
|
|
34
|
+
"lib"
|
|
35
|
+
]
|
|
36
|
+
}
|