@tutti-os/tutti-ops-cli 0.1.3 → 0.1.5
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 +73 -6
- package/dist/appApi.d.ts +11 -1
- package/dist/appApi.js +48 -3
- package/dist/appApi.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +9 -4
- package/dist/config.js.map +1 -1
- package/dist/core/commands.d.ts +2 -0
- package/dist/core/commands.js +71 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/errors.d.ts +7 -0
- package/dist/core/errors.js +16 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/executor.d.ts +32 -0
- package/dist/core/executor.js +133 -0
- package/dist/core/executor.js.map +1 -0
- package/dist/core/input.d.ts +6 -0
- package/dist/core/input.js +163 -0
- package/dist/core/input.js.map +1 -0
- package/dist/core/output.d.ts +6 -0
- package/dist/core/output.js +35 -0
- package/dist/core/output.js.map +1 -0
- package/dist/core/types.d.ts +77 -0
- package/dist/core/types.js +5 -0
- package/dist/core/types.js.map +1 -0
- package/dist/http.d.ts +9 -1
- package/dist/http.js +44 -41
- package/dist/http.js.map +1 -1
- package/dist/index.js +21 -7
- package/dist/index.js.map +1 -1
- package/dist/operations/admin-schemas.d.ts +2 -0
- package/dist/operations/admin-schemas.js +2433 -0
- package/dist/operations/admin-schemas.js.map +1 -0
- package/dist/operations/registry.d.ts +3 -0
- package/dist/operations/registry.js +204 -0
- package/dist/operations/registry.js.map +1 -0
- package/dist/upload.js +1 -0
- package/dist/upload.js.map +1 -1
- package/dist/workflows/app.d.ts +3 -0
- package/dist/workflows/app.js +82 -0
- package/dist/workflows/app.js.map +1 -0
- package/dist/zip.d.ts +19 -0
- package/dist/zip.js +154 -4
- package/dist/zip.js.map +1 -1
- package/package.json +6 -3
- package/skills/tutti-admin/SKILL.md +23 -0
package/dist/zip.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { createReadStream } from 'node:fs';
|
|
3
|
-
import { mkdtemp, readFile, rm, stat } from 'node:fs/promises';
|
|
3
|
+
import { mkdtemp, readFile, realpath, rm, stat } from 'node:fs/promises';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
5
|
+
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
6
6
|
import extract from 'extract-zip';
|
|
7
7
|
export async function inspectZip(filePath) {
|
|
8
8
|
const info = await stat(filePath);
|
|
@@ -12,7 +12,8 @@ export async function inspectZip(filePath) {
|
|
|
12
12
|
if (!filePath.toLowerCase().endsWith('.zip')) {
|
|
13
13
|
throw new Error(`${filePath} must be a .zip file`);
|
|
14
14
|
}
|
|
15
|
-
const [sha256,
|
|
15
|
+
const [sha256, metadata] = await Promise.all([hashFile(filePath), readManifestFromZip(filePath)]);
|
|
16
|
+
const { localizations, manifest } = metadata;
|
|
16
17
|
const appId = manifest.appId ?? manifest.id;
|
|
17
18
|
if (!appId) {
|
|
18
19
|
throw new Error('tutti.app.json missing appId');
|
|
@@ -32,6 +33,7 @@ export async function inspectZip(filePath) {
|
|
|
32
33
|
sizeBytes: info.size,
|
|
33
34
|
version: manifest.version,
|
|
34
35
|
manifest,
|
|
36
|
+
localizations,
|
|
35
37
|
};
|
|
36
38
|
}
|
|
37
39
|
export async function hashFile(filePath) {
|
|
@@ -50,12 +52,20 @@ async function readManifestFromZip(filePath) {
|
|
|
50
52
|
await extract(filePath, { dir });
|
|
51
53
|
const candidates = ['tutti.app.json', 'app/tutti.app.json'];
|
|
52
54
|
for (const candidate of candidates) {
|
|
55
|
+
let raw;
|
|
53
56
|
try {
|
|
54
|
-
|
|
57
|
+
const manifestPath = await resolvePathInsidePackage(dir, join(dir, candidate), 'app manifest');
|
|
58
|
+
raw = await readFile(manifestPath, 'utf8');
|
|
55
59
|
}
|
|
56
60
|
catch {
|
|
57
61
|
// Try the next common package layout.
|
|
62
|
+
continue;
|
|
58
63
|
}
|
|
64
|
+
const manifest = parseTuttiAppManifestJSON(raw, candidate);
|
|
65
|
+
return {
|
|
66
|
+
manifest,
|
|
67
|
+
localizations: await readManifestLocalizations(resolve(dir, candidate, '..'), manifest),
|
|
68
|
+
};
|
|
59
69
|
}
|
|
60
70
|
throw new Error('tutti.app.json not found in zip root');
|
|
61
71
|
}
|
|
@@ -63,4 +73,144 @@ async function readManifestFromZip(filePath) {
|
|
|
63
73
|
await rm(dir, { recursive: true, force: true });
|
|
64
74
|
}
|
|
65
75
|
}
|
|
76
|
+
export function parseTuttiAppManifestJSON(raw, label = 'tutti.app.json') {
|
|
77
|
+
const parsed = parseObjectJSON(raw, label);
|
|
78
|
+
const runtime = optionalObject(parsed.runtime, `${label}.runtime`);
|
|
79
|
+
const localizationInfo = optionalObject(parsed.localizationInfo, `${label}.localizationInfo`);
|
|
80
|
+
let additionalLocales;
|
|
81
|
+
if (localizationInfo?.additionalLocales !== undefined) {
|
|
82
|
+
if (!Array.isArray(localizationInfo.additionalLocales)) {
|
|
83
|
+
throw new Error(`${label}.localizationInfo.additionalLocales must be an array`);
|
|
84
|
+
}
|
|
85
|
+
additionalLocales = localizationInfo.additionalLocales.map((item, index) => {
|
|
86
|
+
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
87
|
+
throw new Error(`${label}.localizationInfo.additionalLocales[${index}] must be an object`);
|
|
88
|
+
}
|
|
89
|
+
const descriptor = item;
|
|
90
|
+
return {
|
|
91
|
+
...descriptor,
|
|
92
|
+
locale: optionalString(descriptor.locale, `${label}.localizationInfo.additionalLocales[${index}].locale`),
|
|
93
|
+
file: optionalString(descriptor.file, `${label}.localizationInfo.additionalLocales[${index}].file`),
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
...parsed,
|
|
99
|
+
appId: optionalString(parsed.appId, `${label}.appId`),
|
|
100
|
+
id: optionalString(parsed.id, `${label}.id`),
|
|
101
|
+
version: optionalString(parsed.version, `${label}.version`),
|
|
102
|
+
name: optionalString(parsed.name, `${label}.name`),
|
|
103
|
+
description: optionalString(parsed.description, `${label}.description`),
|
|
104
|
+
tags: optionalStringArray(parsed.tags, `${label}.tags`),
|
|
105
|
+
...(runtime
|
|
106
|
+
? {
|
|
107
|
+
runtime: {
|
|
108
|
+
...runtime,
|
|
109
|
+
bootstrap: optionalString(runtime.bootstrap, `${label}.runtime.bootstrap`),
|
|
110
|
+
healthcheckPath: optionalString(runtime.healthcheckPath, `${label}.runtime.healthcheckPath`),
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
: {}),
|
|
114
|
+
...(localizationInfo
|
|
115
|
+
? {
|
|
116
|
+
localizationInfo: {
|
|
117
|
+
...localizationInfo,
|
|
118
|
+
defaultLocale: optionalString(localizationInfo.defaultLocale, `${label}.localizationInfo.defaultLocale`),
|
|
119
|
+
additionalLocales,
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export async function readManifestLocalizations(manifestDir, manifest) {
|
|
126
|
+
const descriptors = manifest.localizationInfo?.additionalLocales ?? [];
|
|
127
|
+
const seenLocales = new Set();
|
|
128
|
+
const localizations = [];
|
|
129
|
+
for (const descriptor of descriptors) {
|
|
130
|
+
const locale = descriptor.locale?.trim() ?? '';
|
|
131
|
+
const file = descriptor.file?.trim() ?? '';
|
|
132
|
+
if (!locale || !file) {
|
|
133
|
+
throw new Error('tutti.app.json localizationInfo contains an empty locale or file');
|
|
134
|
+
}
|
|
135
|
+
const localeKey = locale.replaceAll('_', '-').toLowerCase();
|
|
136
|
+
if (seenLocales.has(localeKey)) {
|
|
137
|
+
throw new Error(`tutti.app.json contains duplicate localization locale ${locale}`);
|
|
138
|
+
}
|
|
139
|
+
seenLocales.add(localeKey);
|
|
140
|
+
const localizationPath = await resolvePathInsidePackage(manifestDir, resolve(manifestDir, file), `localization file ${file}`);
|
|
141
|
+
const parsed = parseLocalizationManifest(await readFile(localizationPath, 'utf8'), file);
|
|
142
|
+
localizations.push({
|
|
143
|
+
locale,
|
|
144
|
+
name: parsed.name,
|
|
145
|
+
description: parsed.description,
|
|
146
|
+
tags: parsed.tags,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return localizations;
|
|
150
|
+
}
|
|
151
|
+
async function resolvePathInsidePackage(root, target, label) {
|
|
152
|
+
const lexicalRoot = resolve(root);
|
|
153
|
+
const lexicalTarget = resolve(target);
|
|
154
|
+
const lexicalRelativePath = relative(lexicalRoot, lexicalTarget);
|
|
155
|
+
if (lexicalRelativePath.startsWith('..') || isAbsolute(lexicalRelativePath)) {
|
|
156
|
+
throw new Error(`${label} escapes the app package`);
|
|
157
|
+
}
|
|
158
|
+
const [realRoot, realTarget] = await Promise.all([realpath(lexicalRoot), realpath(lexicalTarget)]);
|
|
159
|
+
const relativePath = relative(realRoot, realTarget);
|
|
160
|
+
if (relativePath.startsWith('..') || isAbsolute(relativePath)) {
|
|
161
|
+
throw new Error(`${label} escapes the app package`);
|
|
162
|
+
}
|
|
163
|
+
return realTarget;
|
|
164
|
+
}
|
|
165
|
+
function parseObjectJSON(raw, label) {
|
|
166
|
+
const parsed = JSON.parse(raw);
|
|
167
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
168
|
+
throw new Error(`${label} must contain a JSON object`);
|
|
169
|
+
}
|
|
170
|
+
return parsed;
|
|
171
|
+
}
|
|
172
|
+
function parseLocalizationManifest(raw, file) {
|
|
173
|
+
const parsed = parseObjectJSON(raw, file);
|
|
174
|
+
const name = optionalString(parsed.name, `${file}.name`);
|
|
175
|
+
const description = optionalString(parsed.description, `${file}.description`);
|
|
176
|
+
const tags = optionalStringArray(parsed.tags, `${file}.tags`) ?? [];
|
|
177
|
+
if (!name && !description && tags.length === 0) {
|
|
178
|
+
throw new Error(`${file} must define a localized name, description, or tags`);
|
|
179
|
+
}
|
|
180
|
+
return { name, description, tags };
|
|
181
|
+
}
|
|
182
|
+
function optionalObject(value, label) {
|
|
183
|
+
if (value === undefined) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
187
|
+
throw new Error(`${label} must be an object`);
|
|
188
|
+
}
|
|
189
|
+
return value;
|
|
190
|
+
}
|
|
191
|
+
function optionalStringArray(value, label) {
|
|
192
|
+
if (value === undefined) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
if (!Array.isArray(value)) {
|
|
196
|
+
throw new Error(`${label} must be an array of strings`);
|
|
197
|
+
}
|
|
198
|
+
return value
|
|
199
|
+
.map((item, index) => {
|
|
200
|
+
if (typeof item !== 'string') {
|
|
201
|
+
throw new Error(`${label}[${index}] must be a string`);
|
|
202
|
+
}
|
|
203
|
+
return item.trim();
|
|
204
|
+
})
|
|
205
|
+
.filter(Boolean);
|
|
206
|
+
}
|
|
207
|
+
function optionalString(value, label) {
|
|
208
|
+
if (value === undefined) {
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
if (typeof value !== 'string') {
|
|
212
|
+
throw new Error(`${label} must be a string`);
|
|
213
|
+
}
|
|
214
|
+
return value.trim() || undefined;
|
|
215
|
+
}
|
|
66
216
|
//# sourceMappingURL=zip.js.map
|
package/dist/zip.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zip.js","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"zip.js","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,OAAO,MAAM,aAAa,CAAC;AA6BlC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,gBAAgB,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,sBAAsB,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClG,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC;IAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,EAAE,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,OAAO;QACL,KAAK;QACL,MAAM;QACN,SAAS,EAAE,IAAI,CAAC,IAAI;QACpB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,QAAQ;QACR,aAAa;KACd,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB;IAC7C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,gBAAgB,CAAC,QAAQ,CAAC;aACvB,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;aACzC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;aACnB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IAIjD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC5D,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACjC,MAAM,UAAU,GAAG,CAAC,gBAAgB,EAAE,oBAAoB,CAAC,CAAC;QAC5D,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,IAAI,GAAW,CAAC;YAChB,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,cAAc,CAAC,CAAC;gBAC/F,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACP,sCAAsC;gBACtC,SAAS;YACX,CAAC;YACD,MAAM,QAAQ,GAAG,yBAAyB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC3D,OAAO;gBACL,QAAQ;gBACR,aAAa,EAAE,MAAM,yBAAyB,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;aACxF,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,GAAW,EAAE,KAAK,GAAG,gBAAgB;IAC7E,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC;IACnE,MAAM,gBAAgB,GAAG,cAAc,CAAC,MAAM,CAAC,gBAAgB,EAAE,GAAG,KAAK,mBAAmB,CAAC,CAAC;IAE9F,IAAI,iBAAyF,CAAC;IAC9F,IAAI,gBAAgB,EAAE,iBAAiB,KAAK,SAAS,EAAE,CAAC;QACtD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,sDAAsD,CAAC,CAAC;QAClF,CAAC;QACD,iBAAiB,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACzE,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7D,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,uCAAuC,KAAK,qBAAqB,CAAC,CAAC;YAC7F,CAAC;YACD,MAAM,UAAU,GAAG,IAA+B,CAAC;YACnD,OAAO;gBACL,GAAG,UAAU;gBACb,MAAM,EAAE,cAAc,CACpB,UAAU,CAAC,MAAM,EACjB,GAAG,KAAK,uCAAuC,KAAK,UAAU,CAC/D;gBACD,IAAI,EAAE,cAAc,CAClB,UAAU,CAAC,IAAI,EACf,GAAG,KAAK,uCAAuC,KAAK,QAAQ,CAC7D;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,GAAG,MAAM;QACT,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,KAAK,QAAQ,CAAC;QACrD,EAAE,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,KAAK,KAAK,CAAC;QAC5C,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,KAAK,UAAU,CAAC;QAC3D,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC;QAClD,WAAW,EAAE,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,KAAK,cAAc,CAAC;QACvE,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC;QACvD,GAAG,CAAC,OAAO;YACT,CAAC,CAAC;gBACE,OAAO,EAAE;oBACP,GAAG,OAAO;oBACV,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,KAAK,oBAAoB,CAAC;oBAC1E,eAAe,EAAE,cAAc,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,KAAK,0BAA0B,CAAC;iBAC7F;aACF;YACH,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,gBAAgB;YAClB,CAAC,CAAC;gBACE,gBAAgB,EAAE;oBAChB,GAAG,gBAAgB;oBACnB,aAAa,EAAE,cAAc,CAC3B,gBAAgB,CAAC,aAAa,EAC9B,GAAG,KAAK,iCAAiC,CAC1C;oBACD,iBAAiB;iBAClB;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACY,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,WAAmB,EACnB,QAA0B;IAE1B,MAAM,WAAW,GAAG,QAAQ,CAAC,gBAAgB,EAAE,iBAAiB,IAAI,EAAE,CAAC;IACvE,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,MAAM,aAAa,GAA2B,EAAE,CAAC;IACjD,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAC3C,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5D,IAAI,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,yDAAyD,MAAM,EAAE,CAAC,CAAC;QACrF,CAAC;QACD,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3B,MAAM,gBAAgB,GAAG,MAAM,wBAAwB,CACrD,WAAW,EACX,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,EAC1B,qBAAqB,IAAI,EAAE,CAC5B,CAAC;QACF,MAAM,MAAM,GAAG,yBAAyB,CAAC,MAAM,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;QACzF,aAAa,CAAC,IAAI,CAAC;YACjB,MAAM;YACN,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,KAAK,UAAU,wBAAwB,CAAC,IAAY,EAAE,MAAc,EAAE,KAAa;IACjF,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IACjE,IAAI,mBAAmB,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,0BAA0B,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IACnG,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACpD,IAAI,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,0BAA0B,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,eAAe,CAAC,GAAW,EAAE,KAAa;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;IAC1C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,6BAA6B,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAiC,CAAC;AAC3C,CAAC;AAED,SAAS,yBAAyB,CAAC,GAAW,EAAE,IAAY;IAC1D,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,IAAI,cAAc,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IACpE,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,qDAAqD,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,cAAc,CAAC,KAAc,EAAE,KAAa;IACnD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,KAAgC,CAAC;AAC1C,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAc,EAAE,KAAa;IACxD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,8BAA8B,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACnB,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,IAAI,KAAK,oBAAoB,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;IACrB,CAAC,CAAC;SACD,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,cAAc,CAAC,KAAc,EAAE,KAAa;IACnD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,mBAAmB,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC;AACnC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tutti-os/tutti-ops-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Internal CLI for Tutti operations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"tutti-ops": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"skills"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
|
-
"build": "tsc -p tsconfig.json",
|
|
14
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
14
15
|
"dev": "tsx src/index.ts",
|
|
16
|
+
"prepack": "npm run build",
|
|
17
|
+
"test": "node --import tsx --test test/*.test.ts",
|
|
15
18
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
16
19
|
},
|
|
17
20
|
"dependencies": {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tutti-admin
|
|
3
|
+
description: Operate Tutti admin through tutti-ops: discover commands, query admin data, preview authorized writes, and publish App packages. Use for admin operations across access, commerce, apps, market, LLM, release, computers, tasks, and diagnostics.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tutti admin operations
|
|
7
|
+
|
|
8
|
+
1. **Discover**: run `tutti-ops capabilities --module <module>` and `tutti-ops schema <operation-id>`. Select an implemented operation; blocked entries explain the protocol gap. The installed schema is the source of truth for command names, required fields, effects, and pagination support.
|
|
9
|
+
2. **Scope**: supply the exact resource IDs from the user's request or a read query. `--brand-id`, `--scope-app-id`, App Center `--app-id`, market `--target`, and package `--source` are different scopes. Use typed flags for short inputs and `--data @file.json` / `--params @file.json` for structured input; request fields keep their wire spelling. Boolean flags take `true` or `false`.
|
|
10
|
+
3. **Authenticate**: use `auth login` / `TUTTI_ADMIN_TOKEN`, then inspect `auth status`. Upgraded servers return `token.access_mode`: `read` means read-only, `write` means full access within the creator's current permissions. Older servers or gateways may reject typed keys; report that dependency before retrying. `--mode read` adds a local restriction and does not change the credential. Session access can use an explicitly supplied `TUTTI_ADMIN_COOKIE`; the CLI does not obtain browser cookies or switch credentials after a denial.
|
|
11
|
+
4. **Read**: execute the operation. For supported pagination, use `--page-all --page-limit 10`; inspect `meta.complete` and the exit code before claiming coverage of the whole collection. For unsupported pagination, use schema fields to request each page explicitly.
|
|
12
|
+
5. **Write**: first run the same canonical command with `--dry-run`. This validates locally and makes no network request. Compare scope, request fields and effects with the user's existing authorization, then run with `--yes`. The dry-run does not establish server permission or current resource state. Preserve optimistic lock fields from a fresh read.
|
|
13
|
+
6. **Report**: read stdout on success and stderr on failure. Exit 6 means partial or uncertain outcome; inspect returned items/request history and query current state before another write. `meta.complete: null` means HTTP returned successfully but business completion was not inferred. Deployment and approval may require later reads. Report executed changes and any remaining state explicitly.
|
|
14
|
+
|
|
15
|
+
## Operation branches
|
|
16
|
+
|
|
17
|
+
- **App packages**: use `app +validate`, `app +inspect`, `app +release`, `app +sync-metadata`, `app +publish`, `app +create`, or `app +edit`; discover flags via schema. Release preserves upload → metadata verification → version creation → optional publish/latest ordering. Failed workflows include HTTP request history; object uploads can need separate inspection.
|
|
18
|
+
- **LLM documents**: read the whole document and base version before editing. Preserve unrelated/default/user fields; route edits must respect the server's editable routes. Atomic publish sends the supplied full document. Validation is a separate remote read command; the existing backend may still require write permission to validate.
|
|
19
|
+
- **Management keys**: inspect `schema admin-token.create`; choose `access_mode=read` unless the authorized task requires full access. Creation defaults to read and checks backend support first. If the returned mode mismatches, use the returned token ID to inspect its state and revoke it within the task authorization; do not retry creation blindly. Permission type is immutable, so replacement requires a new key.
|
|
20
|
+
- **Sensitive results**: token creation, login-code plaintext, client config snippets and upload credentials are redacted by default. Use `--show-secrets` only when the authorized task needs those values, and keep them out of reports/logs. A dry-run always redacts secrets.
|
|
21
|
+
- **Legacy scripts**: old `app list/detail/create/edit/release/...` entries retain their original output and flags. Their `--brand` flag does not enforce brand isolation. Use canonical commands for dry-run, confirmation, schema, structured errors and output formats.
|
|
22
|
+
|
|
23
|
+
Completion: every requested action has either a verified result, an explicit partial/unknown state with the next read to resolve it, or a concrete blocked capability from the installed CLI.
|