@git.zone/tsdeploy 0.2.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.
@@ -0,0 +1,44 @@
1
+ {
2
+ "@git.zone/cli": {
3
+ "schemaVersion": 2,
4
+ "projectType": "npm",
5
+ "module": {
6
+ "githost": "code.foss.global",
7
+ "gitscope": "git.zone",
8
+ "gitrepo": "tsdeploy",
9
+ "description": "Cloudly and Onebox first deployment orchestration CLI for git.zone TypeScript tooling.",
10
+ "npmPackagename": "@git.zone/tsdeploy",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "deployment",
14
+ "Cloudly",
15
+ "Onebox",
16
+ "serve.zone",
17
+ "TypeScript",
18
+ "CLI",
19
+ "git.zone"
20
+ ]
21
+ },
22
+ "release": {
23
+ "targets": {
24
+ "git": {
25
+ "enabled": true,
26
+ "remote": "origin",
27
+ "pushBranch": true,
28
+ "pushTags": true
29
+ },
30
+ "npm": {
31
+ "enabled": true,
32
+ "registries": [
33
+ "https://registry.npmjs.org"
34
+ ],
35
+ "accessLevel": "public",
36
+ "alreadyPublished": "success"
37
+ }
38
+ }
39
+ }
40
+ },
41
+ "@ship.zone/szci": {
42
+ "npmGlobalTools": []
43
+ }
44
+ }
package/cli.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ process.env.CLI_CALL = 'true';
3
+ const cliTool = await import('./dist_ts/index.js');
4
+ cliTool.runCli();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export declare const commitinfo: {
5
+ name: string;
6
+ version: string;
7
+ description: string;
8
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@git.zone/tsdeploy',
6
+ version: '0.2.0',
7
+ description: 'Cloudly and Onebox first deployment orchestration CLI for git.zone TypeScript tooling.'
8
+ };
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxvQkFBb0I7SUFDMUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHdGQUF3RjtDQUN0RyxDQUFBIn0=
@@ -0,0 +1,23 @@
1
+ import type { ITsDeployDoctorResult, ITsDeployLinkOptions, ITsDeployPlan, ITsDeployProfile, ITsDeployProjectLink, ITsDeployPublicProfile, ITsDeploySaveProfileOptions, ITsDeployStatus, ITsDeployUserState } from './types.js';
2
+ export interface ITsDeployOptions {
3
+ projectDir?: string;
4
+ profileStorePath?: string;
5
+ ephemeralProfileStore?: boolean;
6
+ }
7
+ export declare const redactProfile: (profileArg: ITsDeployProfile) => ITsDeployPublicProfile;
8
+ export declare class TsDeploy {
9
+ readonly projectDir: string;
10
+ private readonly profileStore;
11
+ constructor(optionsArg?: ITsDeployOptions);
12
+ get projectLinkPath(): string;
13
+ private createProfileStore;
14
+ readUserState(): Promise<ITsDeployUserState>;
15
+ saveProfile(optionsArg: ITsDeploySaveProfileOptions): Promise<ITsDeployPublicProfile>;
16
+ getProfile(profileArg?: string): Promise<ITsDeployProfile | undefined>;
17
+ readProjectLink(): Promise<ITsDeployProjectLink | undefined>;
18
+ writeProjectLink(optionsArg: ITsDeployLinkOptions): Promise<ITsDeployProjectLink>;
19
+ private normalizeProjectLink;
20
+ getStatus(): Promise<ITsDeployStatus>;
21
+ buildPlan(): Promise<ITsDeployPlan>;
22
+ doctor(): Promise<ITsDeployDoctorResult>;
23
+ }
@@ -0,0 +1,219 @@
1
+ import * as plugins from './plugins.js';
2
+ const schemaVersion = 1;
3
+ const profileNameRegex = /^[a-zA-Z0-9._-]+$/;
4
+ const normalizeProfileName = (profileArg) => {
5
+ const profile = profileArg.trim();
6
+ if (!profile || !profileNameRegex.test(profile)) {
7
+ throw new Error('Profile names may only contain letters, numbers, dots, dashes, and underscores.');
8
+ }
9
+ return profile;
10
+ };
11
+ const normalizeTarget = (targetArg) => {
12
+ if (targetArg === 'cloudly' || targetArg === 'onebox') {
13
+ return targetArg;
14
+ }
15
+ throw new Error('Target must be either "cloudly" or "onebox".');
16
+ };
17
+ const normalizeOrigin = (originArg) => {
18
+ const url = new URL(originArg);
19
+ url.pathname = url.pathname.replace(/\/$/, '');
20
+ return url.toString().replace(/\/$/, '');
21
+ };
22
+ export const redactProfile = (profileArg) => {
23
+ const { token, ...publicProfile } = profileArg;
24
+ return {
25
+ ...publicProfile,
26
+ hasToken: Boolean(token),
27
+ };
28
+ };
29
+ export class TsDeploy {
30
+ projectDir;
31
+ profileStore;
32
+ constructor(optionsArg = {}) {
33
+ this.projectDir = plugins.path.resolve(optionsArg.projectDir ?? process.cwd());
34
+ this.profileStore = this.createProfileStore(optionsArg);
35
+ }
36
+ get projectLinkPath() {
37
+ return plugins.path.join(this.projectDir, '.nogit', 'tsdeploy.json');
38
+ }
39
+ createProfileStore(optionsArg) {
40
+ if (optionsArg.profileStorePath) {
41
+ return new plugins.smartconfig.KeyValueStore({
42
+ typeArg: 'custom',
43
+ identityArg: '@git.zone/tsdeploy',
44
+ customPath: optionsArg.profileStorePath,
45
+ });
46
+ }
47
+ if (optionsArg.ephemeralProfileStore) {
48
+ return new plugins.smartconfig.KeyValueStore({
49
+ typeArg: 'ephemeral',
50
+ identityArg: '@git.zone/tsdeploy',
51
+ });
52
+ }
53
+ plugins.fsSync.mkdirSync(plugins.path.join(plugins.os.homedir(), '.smartconfig', 'kv', '@git.zone'), { recursive: true });
54
+ return new plugins.smartconfig.KeyValueStore({
55
+ typeArg: 'userHomeDir',
56
+ identityArg: '@git.zone/tsdeploy',
57
+ });
58
+ }
59
+ async readUserState() {
60
+ const state = await this.profileStore.readAll();
61
+ return {
62
+ activeProfile: state.activeProfile,
63
+ profiles: state.profiles ?? {},
64
+ };
65
+ }
66
+ async saveProfile(optionsArg) {
67
+ const name = normalizeProfileName(optionsArg.name);
68
+ const target = normalizeTarget(optionsArg.target);
69
+ const origin = normalizeOrigin(optionsArg.origin);
70
+ const state = await this.readUserState();
71
+ const existingProfile = state.profiles?.[name];
72
+ const now = new Date().toISOString();
73
+ const profile = {
74
+ name,
75
+ target,
76
+ origin,
77
+ token: optionsArg.token ?? existingProfile?.token,
78
+ createdAt: existingProfile?.createdAt ?? now,
79
+ updatedAt: now,
80
+ };
81
+ await this.profileStore.writeAll({
82
+ activeProfile: name,
83
+ profiles: {
84
+ ...(state.profiles ?? {}),
85
+ [name]: profile,
86
+ },
87
+ });
88
+ return redactProfile(profile);
89
+ }
90
+ async getProfile(profileArg) {
91
+ const state = await this.readUserState();
92
+ const profileName = profileArg ? normalizeProfileName(profileArg) : state.activeProfile;
93
+ return profileName ? state.profiles?.[profileName] : undefined;
94
+ }
95
+ async readProjectLink() {
96
+ try {
97
+ const raw = await plugins.fs.readFile(this.projectLinkPath, 'utf8');
98
+ const parsed = JSON.parse(raw);
99
+ return this.normalizeProjectLink(parsed);
100
+ }
101
+ catch (error) {
102
+ if (error.code === 'ENOENT') {
103
+ return undefined;
104
+ }
105
+ throw error;
106
+ }
107
+ }
108
+ async writeProjectLink(optionsArg) {
109
+ const projectLink = this.normalizeProjectLink({
110
+ schemaVersion,
111
+ profile: optionsArg.profile,
112
+ deploymentId: optionsArg.deploymentId,
113
+ serviceId: optionsArg.serviceId,
114
+ });
115
+ await plugins.fs.mkdir(plugins.path.dirname(this.projectLinkPath), { recursive: true });
116
+ await plugins.fs.writeFile(this.projectLinkPath, `${JSON.stringify(projectLink, null, 2)}\n`, 'utf8');
117
+ return projectLink;
118
+ }
119
+ normalizeProjectLink(projectLinkArg) {
120
+ if (projectLinkArg.schemaVersion !== schemaVersion) {
121
+ throw new Error(`Unsupported tsdeploy project link schemaVersion: ${projectLinkArg.schemaVersion}`);
122
+ }
123
+ const projectLink = {
124
+ schemaVersion,
125
+ profile: normalizeProfileName(projectLinkArg.profile),
126
+ };
127
+ if (projectLinkArg.deploymentId) {
128
+ projectLink.deploymentId = String(projectLinkArg.deploymentId);
129
+ }
130
+ if (projectLinkArg.serviceId) {
131
+ projectLink.serviceId = String(projectLinkArg.serviceId);
132
+ }
133
+ return projectLink;
134
+ }
135
+ async getStatus() {
136
+ const projectLink = await this.readProjectLink();
137
+ const state = await this.readUserState();
138
+ const activeProfile = projectLink?.profile ?? state.activeProfile;
139
+ const profile = activeProfile ? state.profiles?.[activeProfile] : undefined;
140
+ const warnings = [];
141
+ if (!projectLink) {
142
+ warnings.push('No .nogit/tsdeploy.json project link found. Run tsdeploy init or tsdeploy link.');
143
+ }
144
+ if (activeProfile && !profile) {
145
+ warnings.push(`Profile "${activeProfile}" is not configured. Run tsdeploy login --profile ${activeProfile}.`);
146
+ }
147
+ if (profile && !profile.token) {
148
+ warnings.push(`Profile "${profile.name}" has no token. Remote deploy mutations remain unavailable.`);
149
+ }
150
+ return {
151
+ projectDir: this.projectDir,
152
+ hasProjectLink: Boolean(projectLink),
153
+ projectLink,
154
+ activeProfile,
155
+ profile: profile ? redactProfile(profile) : undefined,
156
+ warnings,
157
+ };
158
+ }
159
+ async buildPlan() {
160
+ const status = await this.getStatus();
161
+ const actions = [];
162
+ if (status.projectLink && status.profile) {
163
+ actions.push({
164
+ type: 'inspect',
165
+ title: 'Inspect linked deployment',
166
+ description: 'Resolve the linked Cloudly/Onebox deployment and compare desired app metadata once typed deployment APIs are verified.',
167
+ });
168
+ }
169
+ else {
170
+ actions.push({
171
+ type: 'noop',
172
+ title: 'Complete local setup',
173
+ description: 'Create a profile and project link before a deployment plan can inspect remote state.',
174
+ });
175
+ }
176
+ actions.push({
177
+ type: 'noop',
178
+ title: 'Deployment mutation disabled',
179
+ description: 'tsdeploy MVP does not install, update, or upgrade apps until Cloudly/Onebox deployment contracts are verified upstream.',
180
+ });
181
+ return {
182
+ schemaVersion,
183
+ projectDir: this.projectDir,
184
+ mutationAllowed: false,
185
+ summary: 'Safe planning only: no remote deployment mutations will be executed.',
186
+ profile: status.activeProfile,
187
+ deploymentId: status.projectLink?.deploymentId,
188
+ serviceId: status.projectLink?.serviceId,
189
+ actions,
190
+ warnings: status.warnings,
191
+ };
192
+ }
193
+ async doctor() {
194
+ const status = await this.getStatus();
195
+ const checks = [
196
+ {
197
+ name: 'project-link',
198
+ ok: status.hasProjectLink,
199
+ message: status.hasProjectLink ? 'Project link exists at .nogit/tsdeploy.json.' : 'Project is not linked yet.',
200
+ },
201
+ {
202
+ name: 'profile',
203
+ ok: Boolean(status.profile),
204
+ message: status.profile ? `Profile "${status.profile.name}" targets ${status.profile.target}.` : 'No matching profile is configured.',
205
+ },
206
+ {
207
+ name: 'remote-mutations',
208
+ ok: true,
209
+ message: 'Remote deployment mutations are intentionally disabled in this MVP.',
210
+ },
211
+ ];
212
+ return {
213
+ ok: checks.every(check => check.ok),
214
+ checks,
215
+ warnings: status.warnings,
216
+ };
217
+ }
218
+ }
219
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50c2RlcGxveS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMudHNkZXBsb3kudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFvQnhDLE1BQU0sYUFBYSxHQUFHLENBQVUsQ0FBQztBQUNqQyxNQUFNLGdCQUFnQixHQUFHLG1CQUFtQixDQUFDO0FBRTdDLE1BQU0sb0JBQW9CLEdBQUcsQ0FBQyxVQUFrQixFQUFVLEVBQUU7SUFDMUQsTUFBTSxPQUFPLEdBQUcsVUFBVSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ2xDLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUNoRCxNQUFNLElBQUksS0FBSyxDQUFDLGlGQUFpRixDQUFDLENBQUM7SUFDckcsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUMsQ0FBQztBQUVGLE1BQU0sZUFBZSxHQUFHLENBQUMsU0FBaUIsRUFBbUIsRUFBRTtJQUM3RCxJQUFJLFNBQVMsS0FBSyxTQUFTLElBQUksU0FBUyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ3RELE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFDRCxNQUFNLElBQUksS0FBSyxDQUFDLDhDQUE4QyxDQUFDLENBQUM7QUFDbEUsQ0FBQyxDQUFDO0FBRUYsTUFBTSxlQUFlLEdBQUcsQ0FBQyxTQUFpQixFQUFVLEVBQUU7SUFDcEQsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDL0IsR0FBRyxDQUFDLFFBQVEsR0FBRyxHQUFHLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDL0MsT0FBTyxHQUFHLENBQUMsUUFBUSxFQUFFLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLENB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
@@ -0,0 +1 @@
1
+ export declare const runCli: () => Promise<void>;
package/dist_ts/cli.js ADDED
@@ -0,0 +1,107 @@
1
+ import * as plugins from './plugins.js';
2
+ import { TsDeploy } from './classes.tsdeploy.js';
3
+ const getStringArg = (argvArg, keyArg) => {
4
+ const value = argvArg[keyArg];
5
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
6
+ };
7
+ const printJson = (valueArg) => {
8
+ console.log(JSON.stringify(valueArg, null, 2));
9
+ };
10
+ const requireStringArg = (argvArg, keyArg) => {
11
+ const value = getStringArg(argvArg, keyArg);
12
+ if (!value) {
13
+ throw new Error(`Missing required --${keyArg} option.`);
14
+ }
15
+ return value;
16
+ };
17
+ const createTsDeploy = () => new TsDeploy({ projectDir: process.cwd() });
18
+ const getSmartcliArgv = () => {
19
+ if (process.argv.length === 0)
20
+ return undefined;
21
+ if (process.argv[0] === process.execPath)
22
+ return undefined;
23
+ return [process.execPath, 'tsdeploy', ...process.argv];
24
+ };
25
+ const runLogin = async (argvArg) => {
26
+ const tsDeploy = createTsDeploy();
27
+ const profile = await tsDeploy.saveProfile({
28
+ name: requireStringArg(argvArg, 'profile'),
29
+ target: requireStringArg(argvArg, 'target'),
30
+ origin: requireStringArg(argvArg, 'origin'),
31
+ token: getStringArg(argvArg, 'token') ?? process.env.TSDEPLOY_TOKEN,
32
+ });
33
+ printJson({ ok: true, profile });
34
+ };
35
+ const runInit = async (argvArg) => {
36
+ const tsDeploy = createTsDeploy();
37
+ const profile = getStringArg(argvArg, 'profile') ?? (await tsDeploy.readUserState()).activeProfile;
38
+ if (!profile) {
39
+ throw new Error('No profile specified and no active profile exists. Run tsdeploy login first or pass --profile.');
40
+ }
41
+ const projectLink = await tsDeploy.writeProjectLink({
42
+ profile,
43
+ deploymentId: getStringArg(argvArg, 'deploymentId'),
44
+ serviceId: getStringArg(argvArg, 'serviceId'),
45
+ });
46
+ printJson({ ok: true, projectLink });
47
+ };
48
+ const runLink = async (argvArg) => {
49
+ const deploymentId = getStringArg(argvArg, 'deploymentId');
50
+ const serviceId = getStringArg(argvArg, 'serviceId');
51
+ if (!deploymentId && !serviceId) {
52
+ throw new Error('Missing --deploymentId or --serviceId.');
53
+ }
54
+ const tsDeploy = createTsDeploy();
55
+ const profile = getStringArg(argvArg, 'profile') ?? (await tsDeploy.readUserState()).activeProfile;
56
+ if (!profile) {
57
+ throw new Error('No profile specified and no active profile exists. Run tsdeploy login first or pass --profile.');
58
+ }
59
+ const projectLink = await tsDeploy.writeProjectLink({ profile, deploymentId, serviceId });
60
+ printJson({ ok: true, projectLink });
61
+ };
62
+ const runStatus = async () => {
63
+ printJson(await createTsDeploy().getStatus());
64
+ };
65
+ const runPlan = async () => {
66
+ printJson(await createTsDeploy().buildPlan());
67
+ };
68
+ const runDoctor = async () => {
69
+ printJson(await createTsDeploy().doctor());
70
+ };
71
+ const printHelp = () => {
72
+ console.log(`
73
+ Usage: tsdeploy <command> [options]
74
+
75
+ Commands:
76
+ login --profile <name> --target <cloudly|onebox> --origin <url> [--token <token>]
77
+ init [--profile <name>] [--deploymentId <id>] [--serviceId <id>]
78
+ link [--profile <name>] (--deploymentId <id> | --serviceId <id>)
79
+ status
80
+ doctor
81
+ plan
82
+
83
+ Notes:
84
+ tsdeploy stores only the project pointer in .nogit/tsdeploy.json.
85
+ Remote deployment mutations are disabled until Cloudly/Onebox APIs are verified.
86
+ `);
87
+ };
88
+ export const runCli = async () => {
89
+ const cli = new plugins.smartcli.Smartcli();
90
+ cli.standardCommand().subscribe(async (argvArg) => {
91
+ const positionalCommand = Array.isArray(argvArg._) ? argvArg._[0] : undefined;
92
+ if (argvArg.help || argvArg.h || positionalCommand === 'help') {
93
+ printHelp();
94
+ return;
95
+ }
96
+ await runStatus();
97
+ });
98
+ cli.addCommand('login').subscribe(runLogin);
99
+ cli.addCommand('init').subscribe(runInit);
100
+ cli.addCommand('link').subscribe(runLink);
101
+ cli.addCommand('status').subscribe(runStatus);
102
+ cli.addCommand('doctor').subscribe(runDoctor);
103
+ cli.addCommand('plan').subscribe(runPlan);
104
+ cli.addCommand('help').subscribe(async () => printHelp());
105
+ cli.startParse(getSmartcliArgv());
106
+ };
107
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUVqRCxNQUFNLFlBQVksR0FBRyxDQUFDLE9BQVksRUFBRSxNQUFjLEVBQXNCLEVBQUU7SUFDeEUsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzlCLE9BQU8sT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFDOUUsQ0FBQyxDQUFDO0FBRUYsTUFBTSxTQUFTLEdBQUcsQ0FBQyxRQUFpQixFQUFRLEVBQUU7SUFDNUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNqRCxDQUFDLENBQUM7QUFFRixNQUFNLGdCQUFnQixHQUFHLENBQUMsT0FBWSxFQUFFLE1BQWMsRUFBVSxFQUFFO0lBQ2hFLE1BQU0sS0FBSyxHQUFHLFlBQVksQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDNUMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1gsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsTUFBTSxVQUFVLENBQUMsQ0FBQztJQUMxRCxDQUFDO0lBQ0QsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDLENBQUM7QUFFRixNQUFNLGNBQWMsR0FBRyxHQUFhLEVBQUUsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxFQUFFLFVBQVUsRUFBRSxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBRW5GLE1BQU0sZUFBZSxH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VCxDQUFDO1FBQ0QsTUFBTSxTQUFTLEVBQUUsQ0FBQztJQUNwQixDQUFDLENBQUMsQ0FBQztJQUNILEdBQUcsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQzVDLEdBQUcsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQzlDLEdBQUcsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQzlDLEdBQUcsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsU0FBUyxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQztJQUMxRCxHQUFHLENBQUMsVUFBVSxDQUFDLGVBQWUsRUFBRSxDQUFDLENBQUM7QUFDcEMsQ0FBQyxDQUFDIn0=
@@ -0,0 +1,3 @@
1
+ export * from './types.js';
2
+ export * from './classes.tsdeploy.js';
3
+ export * from './cli.js';
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export * from './classes.tsdeploy.js';
3
+ export * from './cli.js';
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLFlBQVksQ0FBQztBQUMzQixjQUFjLHVCQUF1QixDQUFDO0FBQ3RDLGNBQWMsVUFBVSxDQUFDIn0=
@@ -0,0 +1,8 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as fsSync from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ export { fs, fsSync, os, path };
6
+ import * as smartcli from '@push.rocks/smartcli';
7
+ import * as smartconfig from '@push.rocks/smartconfig';
8
+ export { smartcli, smartconfig };
@@ -0,0 +1,11 @@
1
+ // node native scope
2
+ import * as fs from 'node:fs/promises';
3
+ import * as fsSync from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
6
+ export { fs, fsSync, os, path };
7
+ // @push.rocks scope
8
+ import * as smartcli from '@push.rocks/smartcli';
9
+ import * as smartconfig from '@push.rocks/smartconfig';
10
+ export { smartcli, smartconfig };
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDdkMsT0FBTyxLQUFLLE1BQU0sTUFBTSxTQUFTLENBQUM7QUFDbEMsT0FBTyxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDOUIsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFFbEMsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxDQUFDO0FBRWhDLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFFdkQsT0FBTyxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsQ0FBQyJ9
@@ -0,0 +1,67 @@
1
+ export type TTsDeployTarget = 'cloudly' | 'onebox';
2
+ export interface ITsDeployProfile {
3
+ name: string;
4
+ target: TTsDeployTarget;
5
+ origin: string;
6
+ token?: string;
7
+ createdAt: string;
8
+ updatedAt: string;
9
+ }
10
+ export interface ITsDeployPublicProfile extends Omit<ITsDeployProfile, 'token'> {
11
+ hasToken: boolean;
12
+ }
13
+ export interface ITsDeployUserState {
14
+ activeProfile?: string;
15
+ profiles?: Record<string, ITsDeployProfile>;
16
+ }
17
+ export interface ITsDeployProjectLink {
18
+ schemaVersion: 1;
19
+ profile: string;
20
+ deploymentId?: string;
21
+ serviceId?: string;
22
+ }
23
+ export interface ITsDeploySaveProfileOptions {
24
+ name: string;
25
+ target: TTsDeployTarget;
26
+ origin: string;
27
+ token?: string;
28
+ }
29
+ export interface ITsDeployLinkOptions {
30
+ profile: string;
31
+ deploymentId?: string;
32
+ serviceId?: string;
33
+ }
34
+ export interface ITsDeployStatus {
35
+ projectDir: string;
36
+ hasProjectLink: boolean;
37
+ projectLink?: ITsDeployProjectLink;
38
+ activeProfile?: string;
39
+ profile?: ITsDeployPublicProfile;
40
+ warnings: string[];
41
+ }
42
+ export interface ITsDeployPlanAction {
43
+ type: 'inspect' | 'noop';
44
+ title: string;
45
+ description: string;
46
+ }
47
+ export interface ITsDeployPlan {
48
+ schemaVersion: 1;
49
+ projectDir: string;
50
+ mutationAllowed: false;
51
+ summary: string;
52
+ profile?: string;
53
+ deploymentId?: string;
54
+ serviceId?: string;
55
+ actions: ITsDeployPlanAction[];
56
+ warnings: string[];
57
+ }
58
+ export interface ITsDeployDoctorCheck {
59
+ name: string;
60
+ ok: boolean;
61
+ message: string;
62
+ }
63
+ export interface ITsDeployDoctorResult {
64
+ ok: boolean;
65
+ checks: ITsDeployDoctorCheck[];
66
+ warnings: string[];
67
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy90eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
package/license.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Task Venture Capital GmbH
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/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@git.zone/tsdeploy",
3
+ "version": "0.2.0",
4
+ "private": false,
5
+ "description": "Cloudly and Onebox first deployment orchestration CLI for git.zone TypeScript tooling.",
6
+ "main": "dist_ts/index.js",
7
+ "typings": "dist_ts/index.d.ts",
8
+ "type": "module",
9
+ "bin": {
10
+ "tsdeploy": "./cli.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://code.foss.global/git.zone/tsdeploy.git"
15
+ },
16
+ "keywords": [
17
+ "deployment",
18
+ "Cloudly",
19
+ "Onebox",
20
+ "serve.zone",
21
+ "TypeScript",
22
+ "CLI",
23
+ "git.zone"
24
+ ],
25
+ "author": "Task Venture Capital GmbH",
26
+ "license": "MIT",
27
+ "bugs": {
28
+ "url": "https://code.foss.global/git.zone/tsdeploy/issues"
29
+ },
30
+ "homepage": "https://code.foss.global/git.zone/tsdeploy#readme",
31
+ "dependencies": {
32
+ "@push.rocks/smartcli": "^4.3.0",
33
+ "@push.rocks/smartconfig": "^6.1.1"
34
+ },
35
+ "devDependencies": {
36
+ "@git.zone/tsbuild": "^4.4.2",
37
+ "@git.zone/tsrun": "^2.0.4",
38
+ "@git.zone/tstest": "^3.6.6",
39
+ "@types/node": "^25.9.3",
40
+ "typescript": "^6.0.3"
41
+ },
42
+ "files": [
43
+ "ts/**/*",
44
+ "dist/**/*",
45
+ "dist_*/**/*",
46
+ "dist_ts/**/*",
47
+ "assets/**/*",
48
+ "cli.js",
49
+ ".smartconfig.json",
50
+ "readme.md",
51
+ "license.md"
52
+ ],
53
+ "browserslist": [
54
+ "last 1 chrome versions"
55
+ ],
56
+ "scripts": {
57
+ "test": "tstest test/ --verbose --logfile --timeout 120",
58
+ "build": "tsbuild tsfolders",
59
+ "buildDocs": "tsdoc"
60
+ }
61
+ }
package/readme.md ADDED
@@ -0,0 +1,100 @@
1
+ # @git.zone/tsdeploy
2
+
3
+ Cloudly and Onebox first deployment orchestration CLI for git.zone TypeScript tooling.
4
+
5
+ ## Issue Reporting and Security
6
+
7
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add -g @git.zone/tsdeploy
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ `tsdeploy` stores deployment intent locally and produces safe, non-mutating plans. Cloudly or Onebox remains the source of truth for actual deployment instances.
18
+
19
+ Create or update a local profile in the user config store:
20
+
21
+ ```bash
22
+ tsdeploy login --profile prod --target cloudly --origin https://cloudly.example.com
23
+ ```
24
+
25
+ Link a project to a Cloudly or Onebox deployment pointer. Use either `init` for the first pointer write or `link` to replace the current pointer:
26
+
27
+ ```bash
28
+ tsdeploy init --profile prod --deploymentId deployment-123
29
+ # or
30
+ tsdeploy link --profile prod --serviceId service-123
31
+ ```
32
+
33
+ Inspect current local state:
34
+
35
+ ```bash
36
+ tsdeploy status
37
+ tsdeploy doctor
38
+ tsdeploy plan
39
+ ```
40
+
41
+ The project pointer is written to `.nogit/tsdeploy.json` and is intentionally small:
42
+
43
+ ```json
44
+ {
45
+ "schemaVersion": 1,
46
+ "profile": "prod",
47
+ "deploymentId": "deployment-123"
48
+ }
49
+ ```
50
+
51
+ Profiles are stored in the user smartconfig key-value store. Tokens are never printed by `status`, `doctor`, or `plan`.
52
+
53
+ ## Current Scope
54
+
55
+ This MVP does not install, update, upgrade, or mutate remote deployments. It prepares the local UX and state model for Cloudly/Onebox-backed deployment flows while avoiding duplicate release, Docker, App Store, or remote-authority semantics.
56
+
57
+ Remote deployment mutations should only be added after the exact Cloudly and Onebox typed APIs for install, update, upgrade, and status are verified upstream.
58
+
59
+ ## Programmatic API
60
+
61
+ ```typescript
62
+ import { TsDeploy } from '@git.zone/tsdeploy';
63
+
64
+ const tsDeploy = new TsDeploy({ projectDir: process.cwd() });
65
+
66
+ await tsDeploy.saveProfile({
67
+ name: 'prod',
68
+ target: 'cloudly',
69
+ origin: 'https://cloudly.example.com',
70
+ });
71
+
72
+ await tsDeploy.writeProjectLink({
73
+ profile: 'prod',
74
+ deploymentId: 'deployment-123',
75
+ });
76
+
77
+ const plan = await tsDeploy.buildPlan();
78
+ console.log(plan.mutationAllowed); // false
79
+ ```
80
+
81
+ ## License and Legal Information
82
+
83
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license.md](./license.md) file.
84
+
85
+ **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
86
+
87
+ ### Trademarks
88
+
89
+ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
90
+
91
+ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
92
+
93
+ ### Company Information
94
+
95
+ Task Venture Capital GmbH<br>
96
+ Registered at District Court Bremen HRB 35230 HB, Germany
97
+
98
+ For any legal inquiries or further information, please contact us via email at hello@task.vc.
99
+
100
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@git.zone/tsdeploy',
6
+ version: '0.2.0',
7
+ description: 'Cloudly and Onebox first deployment orchestration CLI for git.zone TypeScript tooling.'
8
+ }
@@ -0,0 +1,252 @@
1
+ import * as plugins from './plugins.js';
2
+ import type {
3
+ ITsDeployDoctorResult,
4
+ ITsDeployLinkOptions,
5
+ ITsDeployPlan,
6
+ ITsDeployProfile,
7
+ ITsDeployProjectLink,
8
+ ITsDeployPublicProfile,
9
+ ITsDeploySaveProfileOptions,
10
+ ITsDeployStatus,
11
+ ITsDeployUserState,
12
+ TTsDeployTarget,
13
+ } from './types.js';
14
+
15
+ export interface ITsDeployOptions {
16
+ projectDir?: string;
17
+ profileStorePath?: string;
18
+ ephemeralProfileStore?: boolean;
19
+ }
20
+
21
+ const schemaVersion = 1 as const;
22
+ const profileNameRegex = /^[a-zA-Z0-9._-]+$/;
23
+
24
+ const normalizeProfileName = (profileArg: string): string => {
25
+ const profile = profileArg.trim();
26
+ if (!profile || !profileNameRegex.test(profile)) {
27
+ throw new Error('Profile names may only contain letters, numbers, dots, dashes, and underscores.');
28
+ }
29
+ return profile;
30
+ };
31
+
32
+ const normalizeTarget = (targetArg: string): TTsDeployTarget => {
33
+ if (targetArg === 'cloudly' || targetArg === 'onebox') {
34
+ return targetArg;
35
+ }
36
+ throw new Error('Target must be either "cloudly" or "onebox".');
37
+ };
38
+
39
+ const normalizeOrigin = (originArg: string): string => {
40
+ const url = new URL(originArg);
41
+ url.pathname = url.pathname.replace(/\/$/, '');
42
+ return url.toString().replace(/\/$/, '');
43
+ };
44
+
45
+ export const redactProfile = (profileArg: ITsDeployProfile): ITsDeployPublicProfile => {
46
+ const { token, ...publicProfile } = profileArg;
47
+ return {
48
+ ...publicProfile,
49
+ hasToken: Boolean(token),
50
+ };
51
+ };
52
+
53
+ export class TsDeploy {
54
+ public readonly projectDir: string;
55
+ private readonly profileStore: plugins.smartconfig.KeyValueStore<ITsDeployUserState>;
56
+
57
+ constructor(optionsArg: ITsDeployOptions = {}) {
58
+ this.projectDir = plugins.path.resolve(optionsArg.projectDir ?? process.cwd());
59
+ this.profileStore = this.createProfileStore(optionsArg);
60
+ }
61
+
62
+ public get projectLinkPath(): string {
63
+ return plugins.path.join(this.projectDir, '.nogit', 'tsdeploy.json');
64
+ }
65
+
66
+ private createProfileStore(optionsArg: ITsDeployOptions): plugins.smartconfig.KeyValueStore<ITsDeployUserState> {
67
+ if (optionsArg.profileStorePath) {
68
+ return new plugins.smartconfig.KeyValueStore<ITsDeployUserState>({
69
+ typeArg: 'custom',
70
+ identityArg: '@git.zone/tsdeploy',
71
+ customPath: optionsArg.profileStorePath,
72
+ });
73
+ }
74
+ if (optionsArg.ephemeralProfileStore) {
75
+ return new plugins.smartconfig.KeyValueStore<ITsDeployUserState>({
76
+ typeArg: 'ephemeral',
77
+ identityArg: '@git.zone/tsdeploy',
78
+ });
79
+ }
80
+ plugins.fsSync.mkdirSync(plugins.path.join(plugins.os.homedir(), '.smartconfig', 'kv', '@git.zone'), { recursive: true });
81
+ return new plugins.smartconfig.KeyValueStore<ITsDeployUserState>({
82
+ typeArg: 'userHomeDir',
83
+ identityArg: '@git.zone/tsdeploy',
84
+ });
85
+ }
86
+
87
+ public async readUserState(): Promise<ITsDeployUserState> {
88
+ const state = await this.profileStore.readAll();
89
+ return {
90
+ activeProfile: state.activeProfile,
91
+ profiles: state.profiles ?? {},
92
+ };
93
+ }
94
+
95
+ public async saveProfile(optionsArg: ITsDeploySaveProfileOptions): Promise<ITsDeployPublicProfile> {
96
+ const name = normalizeProfileName(optionsArg.name);
97
+ const target = normalizeTarget(optionsArg.target);
98
+ const origin = normalizeOrigin(optionsArg.origin);
99
+ const state = await this.readUserState();
100
+ const existingProfile = state.profiles?.[name];
101
+ const now = new Date().toISOString();
102
+ const profile: ITsDeployProfile = {
103
+ name,
104
+ target,
105
+ origin,
106
+ token: optionsArg.token ?? existingProfile?.token,
107
+ createdAt: existingProfile?.createdAt ?? now,
108
+ updatedAt: now,
109
+ };
110
+ await this.profileStore.writeAll({
111
+ activeProfile: name,
112
+ profiles: {
113
+ ...(state.profiles ?? {}),
114
+ [name]: profile,
115
+ },
116
+ });
117
+ return redactProfile(profile);
118
+ }
119
+
120
+ public async getProfile(profileArg?: string): Promise<ITsDeployProfile | undefined> {
121
+ const state = await this.readUserState();
122
+ const profileName = profileArg ? normalizeProfileName(profileArg) : state.activeProfile;
123
+ return profileName ? state.profiles?.[profileName] : undefined;
124
+ }
125
+
126
+ public async readProjectLink(): Promise<ITsDeployProjectLink | undefined> {
127
+ try {
128
+ const raw = await plugins.fs.readFile(this.projectLinkPath, 'utf8');
129
+ const parsed = JSON.parse(raw) as ITsDeployProjectLink;
130
+ return this.normalizeProjectLink(parsed);
131
+ } catch (error) {
132
+ if ((error as { code?: string }).code === 'ENOENT') {
133
+ return undefined;
134
+ }
135
+ throw error;
136
+ }
137
+ }
138
+
139
+ public async writeProjectLink(optionsArg: ITsDeployLinkOptions): Promise<ITsDeployProjectLink> {
140
+ const projectLink = this.normalizeProjectLink({
141
+ schemaVersion,
142
+ profile: optionsArg.profile,
143
+ deploymentId: optionsArg.deploymentId,
144
+ serviceId: optionsArg.serviceId,
145
+ });
146
+ await plugins.fs.mkdir(plugins.path.dirname(this.projectLinkPath), { recursive: true });
147
+ await plugins.fs.writeFile(this.projectLinkPath, `${JSON.stringify(projectLink, null, 2)}\n`, 'utf8');
148
+ return projectLink;
149
+ }
150
+
151
+ private normalizeProjectLink(projectLinkArg: ITsDeployProjectLink): ITsDeployProjectLink {
152
+ if (projectLinkArg.schemaVersion !== schemaVersion) {
153
+ throw new Error(`Unsupported tsdeploy project link schemaVersion: ${projectLinkArg.schemaVersion}`);
154
+ }
155
+ const projectLink: ITsDeployProjectLink = {
156
+ schemaVersion,
157
+ profile: normalizeProfileName(projectLinkArg.profile),
158
+ };
159
+ if (projectLinkArg.deploymentId) {
160
+ projectLink.deploymentId = String(projectLinkArg.deploymentId);
161
+ }
162
+ if (projectLinkArg.serviceId) {
163
+ projectLink.serviceId = String(projectLinkArg.serviceId);
164
+ }
165
+ return projectLink;
166
+ }
167
+
168
+ public async getStatus(): Promise<ITsDeployStatus> {
169
+ const projectLink = await this.readProjectLink();
170
+ const state = await this.readUserState();
171
+ const activeProfile = projectLink?.profile ?? state.activeProfile;
172
+ const profile = activeProfile ? state.profiles?.[activeProfile] : undefined;
173
+ const warnings: string[] = [];
174
+ if (!projectLink) {
175
+ warnings.push('No .nogit/tsdeploy.json project link found. Run tsdeploy init or tsdeploy link.');
176
+ }
177
+ if (activeProfile && !profile) {
178
+ warnings.push(`Profile "${activeProfile}" is not configured. Run tsdeploy login --profile ${activeProfile}.`);
179
+ }
180
+ if (profile && !profile.token) {
181
+ warnings.push(`Profile "${profile.name}" has no token. Remote deploy mutations remain unavailable.`);
182
+ }
183
+ return {
184
+ projectDir: this.projectDir,
185
+ hasProjectLink: Boolean(projectLink),
186
+ projectLink,
187
+ activeProfile,
188
+ profile: profile ? redactProfile(profile) : undefined,
189
+ warnings,
190
+ };
191
+ }
192
+
193
+ public async buildPlan(): Promise<ITsDeployPlan> {
194
+ const status = await this.getStatus();
195
+ const actions: ITsDeployPlan['actions'] = [];
196
+ if (status.projectLink && status.profile) {
197
+ actions.push({
198
+ type: 'inspect',
199
+ title: 'Inspect linked deployment',
200
+ description: 'Resolve the linked Cloudly/Onebox deployment and compare desired app metadata once typed deployment APIs are verified.',
201
+ });
202
+ } else {
203
+ actions.push({
204
+ type: 'noop',
205
+ title: 'Complete local setup',
206
+ description: 'Create a profile and project link before a deployment plan can inspect remote state.',
207
+ });
208
+ }
209
+ actions.push({
210
+ type: 'noop',
211
+ title: 'Deployment mutation disabled',
212
+ description: 'tsdeploy MVP does not install, update, or upgrade apps until Cloudly/Onebox deployment contracts are verified upstream.',
213
+ });
214
+ return {
215
+ schemaVersion,
216
+ projectDir: this.projectDir,
217
+ mutationAllowed: false,
218
+ summary: 'Safe planning only: no remote deployment mutations will be executed.',
219
+ profile: status.activeProfile,
220
+ deploymentId: status.projectLink?.deploymentId,
221
+ serviceId: status.projectLink?.serviceId,
222
+ actions,
223
+ warnings: status.warnings,
224
+ };
225
+ }
226
+
227
+ public async doctor(): Promise<ITsDeployDoctorResult> {
228
+ const status = await this.getStatus();
229
+ const checks = [
230
+ {
231
+ name: 'project-link',
232
+ ok: status.hasProjectLink,
233
+ message: status.hasProjectLink ? 'Project link exists at .nogit/tsdeploy.json.' : 'Project is not linked yet.',
234
+ },
235
+ {
236
+ name: 'profile',
237
+ ok: Boolean(status.profile),
238
+ message: status.profile ? `Profile "${status.profile.name}" targets ${status.profile.target}.` : 'No matching profile is configured.',
239
+ },
240
+ {
241
+ name: 'remote-mutations',
242
+ ok: true,
243
+ message: 'Remote deployment mutations are intentionally disabled in this MVP.',
244
+ },
245
+ ];
246
+ return {
247
+ ok: checks.every(check => check.ok),
248
+ checks,
249
+ warnings: status.warnings,
250
+ };
251
+ }
252
+ }
package/ts/cli.ts ADDED
@@ -0,0 +1,117 @@
1
+ import * as plugins from './plugins.js';
2
+ import { TsDeploy } from './classes.tsdeploy.js';
3
+
4
+ const getStringArg = (argvArg: any, keyArg: string): string | undefined => {
5
+ const value = argvArg[keyArg];
6
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
7
+ };
8
+
9
+ const printJson = (valueArg: unknown): void => {
10
+ console.log(JSON.stringify(valueArg, null, 2));
11
+ };
12
+
13
+ const requireStringArg = (argvArg: any, keyArg: string): string => {
14
+ const value = getStringArg(argvArg, keyArg);
15
+ if (!value) {
16
+ throw new Error(`Missing required --${keyArg} option.`);
17
+ }
18
+ return value;
19
+ };
20
+
21
+ const createTsDeploy = (): TsDeploy => new TsDeploy({ projectDir: process.cwd() });
22
+
23
+ const getSmartcliArgv = (): string[] | undefined => {
24
+ if (process.argv.length === 0) return undefined;
25
+ if (process.argv[0] === process.execPath) return undefined;
26
+ return [process.execPath, 'tsdeploy', ...process.argv];
27
+ };
28
+
29
+ const runLogin = async (argvArg: any): Promise<void> => {
30
+ const tsDeploy = createTsDeploy();
31
+ const profile = await tsDeploy.saveProfile({
32
+ name: requireStringArg(argvArg, 'profile'),
33
+ target: requireStringArg(argvArg, 'target') as any,
34
+ origin: requireStringArg(argvArg, 'origin'),
35
+ token: getStringArg(argvArg, 'token') ?? process.env.TSDEPLOY_TOKEN,
36
+ });
37
+ printJson({ ok: true, profile });
38
+ };
39
+
40
+ const runInit = async (argvArg: any): Promise<void> => {
41
+ const tsDeploy = createTsDeploy();
42
+ const profile = getStringArg(argvArg, 'profile') ?? (await tsDeploy.readUserState()).activeProfile;
43
+ if (!profile) {
44
+ throw new Error('No profile specified and no active profile exists. Run tsdeploy login first or pass --profile.');
45
+ }
46
+ const projectLink = await tsDeploy.writeProjectLink({
47
+ profile,
48
+ deploymentId: getStringArg(argvArg, 'deploymentId'),
49
+ serviceId: getStringArg(argvArg, 'serviceId'),
50
+ });
51
+ printJson({ ok: true, projectLink });
52
+ };
53
+
54
+ const runLink = async (argvArg: any): Promise<void> => {
55
+ const deploymentId = getStringArg(argvArg, 'deploymentId');
56
+ const serviceId = getStringArg(argvArg, 'serviceId');
57
+ if (!deploymentId && !serviceId) {
58
+ throw new Error('Missing --deploymentId or --serviceId.');
59
+ }
60
+ const tsDeploy = createTsDeploy();
61
+ const profile = getStringArg(argvArg, 'profile') ?? (await tsDeploy.readUserState()).activeProfile;
62
+ if (!profile) {
63
+ throw new Error('No profile specified and no active profile exists. Run tsdeploy login first or pass --profile.');
64
+ }
65
+ const projectLink = await tsDeploy.writeProjectLink({ profile, deploymentId, serviceId });
66
+ printJson({ ok: true, projectLink });
67
+ };
68
+
69
+ const runStatus = async (): Promise<void> => {
70
+ printJson(await createTsDeploy().getStatus());
71
+ };
72
+
73
+ const runPlan = async (): Promise<void> => {
74
+ printJson(await createTsDeploy().buildPlan());
75
+ };
76
+
77
+ const runDoctor = async (): Promise<void> => {
78
+ printJson(await createTsDeploy().doctor());
79
+ };
80
+
81
+ const printHelp = (): void => {
82
+ console.log(`
83
+ Usage: tsdeploy <command> [options]
84
+
85
+ Commands:
86
+ login --profile <name> --target <cloudly|onebox> --origin <url> [--token <token>]
87
+ init [--profile <name>] [--deploymentId <id>] [--serviceId <id>]
88
+ link [--profile <name>] (--deploymentId <id> | --serviceId <id>)
89
+ status
90
+ doctor
91
+ plan
92
+
93
+ Notes:
94
+ tsdeploy stores only the project pointer in .nogit/tsdeploy.json.
95
+ Remote deployment mutations are disabled until Cloudly/Onebox APIs are verified.
96
+ `);
97
+ };
98
+
99
+ export const runCli = async (): Promise<void> => {
100
+ const cli = new plugins.smartcli.Smartcli();
101
+ cli.standardCommand().subscribe(async (argvArg) => {
102
+ const positionalCommand = Array.isArray(argvArg._) ? argvArg._[0] : undefined;
103
+ if (argvArg.help || argvArg.h || positionalCommand === 'help') {
104
+ printHelp();
105
+ return;
106
+ }
107
+ await runStatus();
108
+ });
109
+ cli.addCommand('login').subscribe(runLogin);
110
+ cli.addCommand('init').subscribe(runInit);
111
+ cli.addCommand('link').subscribe(runLink);
112
+ cli.addCommand('status').subscribe(runStatus);
113
+ cli.addCommand('doctor').subscribe(runDoctor);
114
+ cli.addCommand('plan').subscribe(runPlan);
115
+ cli.addCommand('help').subscribe(async () => printHelp());
116
+ cli.startParse(getSmartcliArgv());
117
+ };
package/ts/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './types.js';
2
+ export * from './classes.tsdeploy.js';
3
+ export * from './cli.js';
package/ts/plugins.ts ADDED
@@ -0,0 +1,13 @@
1
+ // node native scope
2
+ import * as fs from 'node:fs/promises';
3
+ import * as fsSync from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
6
+
7
+ export { fs, fsSync, os, path };
8
+
9
+ // @push.rocks scope
10
+ import * as smartcli from '@push.rocks/smartcli';
11
+ import * as smartconfig from '@push.rocks/smartconfig';
12
+
13
+ export { smartcli, smartconfig };
package/ts/types.ts ADDED
@@ -0,0 +1,78 @@
1
+ export type TTsDeployTarget = 'cloudly' | 'onebox';
2
+
3
+ export interface ITsDeployProfile {
4
+ name: string;
5
+ target: TTsDeployTarget;
6
+ origin: string;
7
+ token?: string;
8
+ createdAt: string;
9
+ updatedAt: string;
10
+ }
11
+
12
+ export interface ITsDeployPublicProfile extends Omit<ITsDeployProfile, 'token'> {
13
+ hasToken: boolean;
14
+ }
15
+
16
+ export interface ITsDeployUserState {
17
+ activeProfile?: string;
18
+ profiles?: Record<string, ITsDeployProfile>;
19
+ }
20
+
21
+ export interface ITsDeployProjectLink {
22
+ schemaVersion: 1;
23
+ profile: string;
24
+ deploymentId?: string;
25
+ serviceId?: string;
26
+ }
27
+
28
+ export interface ITsDeploySaveProfileOptions {
29
+ name: string;
30
+ target: TTsDeployTarget;
31
+ origin: string;
32
+ token?: string;
33
+ }
34
+
35
+ export interface ITsDeployLinkOptions {
36
+ profile: string;
37
+ deploymentId?: string;
38
+ serviceId?: string;
39
+ }
40
+
41
+ export interface ITsDeployStatus {
42
+ projectDir: string;
43
+ hasProjectLink: boolean;
44
+ projectLink?: ITsDeployProjectLink;
45
+ activeProfile?: string;
46
+ profile?: ITsDeployPublicProfile;
47
+ warnings: string[];
48
+ }
49
+
50
+ export interface ITsDeployPlanAction {
51
+ type: 'inspect' | 'noop';
52
+ title: string;
53
+ description: string;
54
+ }
55
+
56
+ export interface ITsDeployPlan {
57
+ schemaVersion: 1;
58
+ projectDir: string;
59
+ mutationAllowed: false;
60
+ summary: string;
61
+ profile?: string;
62
+ deploymentId?: string;
63
+ serviceId?: string;
64
+ actions: ITsDeployPlanAction[];
65
+ warnings: string[];
66
+ }
67
+
68
+ export interface ITsDeployDoctorCheck {
69
+ name: string;
70
+ ok: boolean;
71
+ message: string;
72
+ }
73
+
74
+ export interface ITsDeployDoctorResult {
75
+ ok: boolean;
76
+ checks: ITsDeployDoctorCheck[];
77
+ warnings: string[];
78
+ }