@aigne/afs-gce 1.11.0-beta.10
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.md +26 -0
- package/dist/index.d.mts +202 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +751 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 ArcBlock, Inc. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are proprietary
|
|
6
|
+
and confidential. Unauthorized copying, modification, distribution, or use of
|
|
7
|
+
this Software, via any medium, is strictly prohibited.
|
|
8
|
+
|
|
9
|
+
The Software is provided for internal use only within ArcBlock, Inc. and its
|
|
10
|
+
authorized affiliates.
|
|
11
|
+
|
|
12
|
+
## No License Granted
|
|
13
|
+
|
|
14
|
+
No license, express or implied, is granted to any party for any purpose.
|
|
15
|
+
All rights are reserved by ArcBlock, Inc.
|
|
16
|
+
|
|
17
|
+
## Public Artifact Distribution
|
|
18
|
+
|
|
19
|
+
Portions of this Software may be released publicly under separate open-source
|
|
20
|
+
licenses (such as MIT License) through designated public repositories. Such
|
|
21
|
+
public releases are governed by their respective licenses and do not affect
|
|
22
|
+
the proprietary nature of this repository.
|
|
23
|
+
|
|
24
|
+
## Contact
|
|
25
|
+
|
|
26
|
+
For licensing inquiries, contact: legal@arcblock.io
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { AFSAccessMode, AFSBaseProvider, AFSDeleteResult, AFSEntry, AFSExecResult, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSStatResult, AFSWriteEntryPayload, AFSWriteResult, ProviderManifest, RouteContext } from "@aigne/afs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Configuration options for AFSGCE
|
|
7
|
+
*/
|
|
8
|
+
interface AFSGCEOptions {
|
|
9
|
+
/** Module name (used as mount path segment) */
|
|
10
|
+
name?: string;
|
|
11
|
+
/** Module description */
|
|
12
|
+
description?: string;
|
|
13
|
+
/** GCP project ID */
|
|
14
|
+
projectId: string;
|
|
15
|
+
/** GCE zone (e.g., us-central1-a) */
|
|
16
|
+
zone: string;
|
|
17
|
+
/** Access mode */
|
|
18
|
+
accessMode?: "readonly" | "readwrite";
|
|
19
|
+
/** Path to service account key file */
|
|
20
|
+
keyFilename?: string;
|
|
21
|
+
/** Explicit credentials (for programmatic access) */
|
|
22
|
+
credentials?: {
|
|
23
|
+
clientEmail: string;
|
|
24
|
+
privateKey: string;
|
|
25
|
+
};
|
|
26
|
+
/** Cache TTL in seconds (0 = no cache) */
|
|
27
|
+
cacheTtl?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Zod schema for options validation
|
|
31
|
+
*/
|
|
32
|
+
declare const afsgceOptionsSchema: z.ZodType<{
|
|
33
|
+
name: string | undefined;
|
|
34
|
+
description: string | undefined;
|
|
35
|
+
projectId: string;
|
|
36
|
+
zone: string;
|
|
37
|
+
accessMode: "readonly" | "readwrite" | undefined;
|
|
38
|
+
keyFilename: string | undefined;
|
|
39
|
+
credentials: {
|
|
40
|
+
clientEmail: string;
|
|
41
|
+
privateKey: string;
|
|
42
|
+
} | undefined;
|
|
43
|
+
cacheTtl: number | undefined;
|
|
44
|
+
}, unknown, z.core.$ZodTypeInternals<{
|
|
45
|
+
name: string | undefined;
|
|
46
|
+
description: string | undefined;
|
|
47
|
+
projectId: string;
|
|
48
|
+
zone: string;
|
|
49
|
+
accessMode: "readonly" | "readwrite" | undefined;
|
|
50
|
+
keyFilename: string | undefined;
|
|
51
|
+
credentials: {
|
|
52
|
+
clientEmail: string;
|
|
53
|
+
privateKey: string;
|
|
54
|
+
} | undefined;
|
|
55
|
+
cacheTtl: number | undefined;
|
|
56
|
+
}, unknown>>;
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/index.d.ts
|
|
59
|
+
/**
|
|
60
|
+
* AFSGCE Provider using AFSBaseProvider pattern
|
|
61
|
+
*
|
|
62
|
+
* Provides file-system-like access to GCE instances.
|
|
63
|
+
* Uses decorator routing (@List, @Read, @Write, @Delete, @Meta, @Actions, @Explain).
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* const gce = new AFSGCE({
|
|
68
|
+
* projectId: "my-project",
|
|
69
|
+
* zone: "us-central1-a",
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* // Mount to AFS
|
|
73
|
+
* afs.mount(gce);
|
|
74
|
+
*
|
|
75
|
+
* // List instances
|
|
76
|
+
* const result = await afs.list("/modules/gce/instances");
|
|
77
|
+
*
|
|
78
|
+
* // Read instance metadata
|
|
79
|
+
* const meta = await afs.read("/modules/gce/instances/my-vm/metadata.json");
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare class AFSGCE extends AFSBaseProvider {
|
|
83
|
+
readonly name: string;
|
|
84
|
+
readonly description?: string;
|
|
85
|
+
readonly accessMode: AFSAccessMode;
|
|
86
|
+
private readonly client;
|
|
87
|
+
private readonly projectId;
|
|
88
|
+
private readonly zone;
|
|
89
|
+
constructor(options: AFSGCEOptions & {
|
|
90
|
+
uri?: string;
|
|
91
|
+
token?: string;
|
|
92
|
+
auth?: unknown;
|
|
93
|
+
});
|
|
94
|
+
static schema(): z.ZodType<{
|
|
95
|
+
name: string | undefined;
|
|
96
|
+
description: string | undefined;
|
|
97
|
+
projectId: string;
|
|
98
|
+
zone: string;
|
|
99
|
+
accessMode: "readonly" | "readwrite" | undefined;
|
|
100
|
+
keyFilename: string | undefined;
|
|
101
|
+
credentials: {
|
|
102
|
+
clientEmail: string;
|
|
103
|
+
privateKey: string;
|
|
104
|
+
} | undefined;
|
|
105
|
+
cacheTtl: number | undefined;
|
|
106
|
+
}, unknown, z.core.$ZodTypeInternals<{
|
|
107
|
+
name: string | undefined;
|
|
108
|
+
description: string | undefined;
|
|
109
|
+
projectId: string;
|
|
110
|
+
zone: string;
|
|
111
|
+
accessMode: "readonly" | "readwrite" | undefined;
|
|
112
|
+
keyFilename: string | undefined;
|
|
113
|
+
credentials: {
|
|
114
|
+
clientEmail: string;
|
|
115
|
+
privateKey: string;
|
|
116
|
+
} | undefined;
|
|
117
|
+
cacheTtl: number | undefined;
|
|
118
|
+
}, unknown>>;
|
|
119
|
+
/**
|
|
120
|
+
* Provider manifest for URI-based discovery
|
|
121
|
+
*/
|
|
122
|
+
static manifest(): ProviderManifest;
|
|
123
|
+
static load({
|
|
124
|
+
basePath,
|
|
125
|
+
config
|
|
126
|
+
}?: AFSModuleLoadParams): Promise<AFSGCE>;
|
|
127
|
+
private getInstance;
|
|
128
|
+
private listAllInstances;
|
|
129
|
+
private buildInstanceEntry;
|
|
130
|
+
listRoot(_ctx: RouteContext): Promise<AFSListResult>;
|
|
131
|
+
listInstances(_ctx: RouteContext): Promise<AFSListResult>;
|
|
132
|
+
listInstanceChildren(ctx: RouteContext<{
|
|
133
|
+
instanceId: string;
|
|
134
|
+
status?: string;
|
|
135
|
+
}>): Promise<AFSListResult>;
|
|
136
|
+
listInstanceLabels(ctx: RouteContext<{
|
|
137
|
+
instanceId: string;
|
|
138
|
+
status?: string;
|
|
139
|
+
}>): Promise<AFSListResult>;
|
|
140
|
+
listByStatus(_ctx: RouteContext): Promise<AFSListResult>;
|
|
141
|
+
listByStatusFilter(ctx: RouteContext<{
|
|
142
|
+
status: string;
|
|
143
|
+
}>): Promise<AFSListResult>;
|
|
144
|
+
readCapabilities(_ctx: RouteContext): Promise<AFSEntry>;
|
|
145
|
+
readInstanceMetadata(ctx: RouteContext<{
|
|
146
|
+
instanceId: string;
|
|
147
|
+
status?: string;
|
|
148
|
+
}>): Promise<AFSEntry>;
|
|
149
|
+
readInstanceLabel(ctx: RouteContext<{
|
|
150
|
+
instanceId: string;
|
|
151
|
+
labelKey: string;
|
|
152
|
+
status?: string;
|
|
153
|
+
}>): Promise<AFSEntry>;
|
|
154
|
+
metaRoot(ctx: RouteContext): Promise<AFSEntry>;
|
|
155
|
+
metaInstances(ctx: RouteContext): Promise<AFSEntry>;
|
|
156
|
+
metaInstance(ctx: RouteContext<{
|
|
157
|
+
instanceId: string;
|
|
158
|
+
status?: string;
|
|
159
|
+
}>): Promise<AFSEntry>;
|
|
160
|
+
statRoot(ctx: RouteContext): Promise<AFSStatResult>;
|
|
161
|
+
statInstances(ctx: RouteContext): Promise<AFSStatResult>;
|
|
162
|
+
statInstance(ctx: RouteContext<{
|
|
163
|
+
instanceId: string;
|
|
164
|
+
status?: string;
|
|
165
|
+
}>): Promise<AFSStatResult>;
|
|
166
|
+
writeInstanceLabel(ctx: RouteContext<{
|
|
167
|
+
instanceId: string;
|
|
168
|
+
labelKey: string;
|
|
169
|
+
status?: string;
|
|
170
|
+
}>, payload: AFSWriteEntryPayload): Promise<AFSWriteResult>;
|
|
171
|
+
deleteInstanceLabel(ctx: RouteContext<{
|
|
172
|
+
instanceId: string;
|
|
173
|
+
labelKey: string;
|
|
174
|
+
status?: string;
|
|
175
|
+
}>): Promise<AFSDeleteResult>;
|
|
176
|
+
listGlobalActions(ctx: RouteContext): Promise<AFSListResult>;
|
|
177
|
+
refreshAction(_ctx: RouteContext, _args: Record<string, unknown>): Promise<AFSExecResult>;
|
|
178
|
+
listInstanceActions(ctx: RouteContext<{
|
|
179
|
+
instanceId: string;
|
|
180
|
+
status?: string;
|
|
181
|
+
}>): Promise<AFSListResult>;
|
|
182
|
+
startInstanceAction(ctx: RouteContext<{
|
|
183
|
+
instanceId: string;
|
|
184
|
+
status?: string;
|
|
185
|
+
}>, _args: Record<string, unknown>): Promise<AFSExecResult>;
|
|
186
|
+
stopInstanceAction(ctx: RouteContext<{
|
|
187
|
+
instanceId: string;
|
|
188
|
+
status?: string;
|
|
189
|
+
}>, _args: Record<string, unknown>): Promise<AFSExecResult>;
|
|
190
|
+
resetInstanceAction(ctx: RouteContext<{
|
|
191
|
+
instanceId: string;
|
|
192
|
+
status?: string;
|
|
193
|
+
}>, _args: Record<string, unknown>): Promise<AFSExecResult>;
|
|
194
|
+
explainRoot(_ctx: RouteContext): Promise<AFSExplainResult>;
|
|
195
|
+
explainInstance(ctx: RouteContext<{
|
|
196
|
+
instanceId: string;
|
|
197
|
+
status?: string;
|
|
198
|
+
}>): Promise<AFSExplainResult>;
|
|
199
|
+
}
|
|
200
|
+
//#endregion
|
|
201
|
+
export { AFSGCE, type AFSGCEOptions, afsgceOptionsSchema };
|
|
202
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/index.ts"],"mappings":"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"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
import { AFSBaseProvider, AFSNotFoundError, AFSReadonlyError, Actions, Delete, Explain, List, Meta, Read, Stat, Write } from "@aigne/afs";
|
|
2
|
+
import { camelize, optionalize, zodParse } from "@aigne/afs/utils/zod";
|
|
3
|
+
import { InstancesClient } from "@google-cloud/compute";
|
|
4
|
+
import { joinURL } from "ufo";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
//#region src/platform-ref.ts
|
|
8
|
+
/**
|
|
9
|
+
* Generate platform reference with GCP Console URL for an instance
|
|
10
|
+
*
|
|
11
|
+
* @param projectId - GCP project ID
|
|
12
|
+
* @param zone - GCE zone
|
|
13
|
+
* @param instanceName - Instance name
|
|
14
|
+
* @returns Platform reference with console URL
|
|
15
|
+
*/
|
|
16
|
+
function generateInstancePlatformRef(projectId, zone, instanceName) {
|
|
17
|
+
return { consoleUrl: `https://console.cloud.google.com/compute/instancesDetail/zones/${zone}/instances/${instanceName}?project=${projectId}` };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Generate platform reference with GCP Console URL for instances list
|
|
21
|
+
*
|
|
22
|
+
* @param projectId - GCP project ID
|
|
23
|
+
* @returns Platform reference with console URL
|
|
24
|
+
*/
|
|
25
|
+
function generateInstancesListPlatformRef(projectId) {
|
|
26
|
+
return { consoleUrl: `https://console.cloud.google.com/compute/instances?project=${projectId}` };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/types.ts
|
|
31
|
+
/**
|
|
32
|
+
* AFS GCE Provider Types
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* GCE zone validation regex
|
|
36
|
+
* Format: region-zone (e.g., us-central1-a, europe-west1-b)
|
|
37
|
+
*/
|
|
38
|
+
const ZONE_REGEX = /^[a-z]+-[a-z]+\d+-[a-z]$/;
|
|
39
|
+
/**
|
|
40
|
+
* Zod schema for options validation
|
|
41
|
+
*/
|
|
42
|
+
const afsgceOptionsSchema = camelize(z.object({
|
|
43
|
+
name: optionalize(z.string()),
|
|
44
|
+
description: optionalize(z.string()),
|
|
45
|
+
projectId: z.string().min(1),
|
|
46
|
+
zone: z.string().regex(ZONE_REGEX, "Invalid GCE zone format (e.g., us-central1-a)"),
|
|
47
|
+
accessMode: optionalize(z.enum(["readonly", "readwrite"])),
|
|
48
|
+
keyFilename: optionalize(z.string()),
|
|
49
|
+
credentials: optionalize(z.object({
|
|
50
|
+
clientEmail: z.string(),
|
|
51
|
+
privateKey: z.string()
|
|
52
|
+
})),
|
|
53
|
+
cacheTtl: optionalize(z.number().int().min(0))
|
|
54
|
+
}).strict());
|
|
55
|
+
/**
|
|
56
|
+
* GCE instance states
|
|
57
|
+
*/
|
|
58
|
+
const GCE_INSTANCE_STATES = [
|
|
59
|
+
"PROVISIONING",
|
|
60
|
+
"STAGING",
|
|
61
|
+
"RUNNING",
|
|
62
|
+
"STOPPING",
|
|
63
|
+
"STOPPED",
|
|
64
|
+
"SUSPENDING",
|
|
65
|
+
"SUSPENDED",
|
|
66
|
+
"TERMINATED"
|
|
67
|
+
];
|
|
68
|
+
/**
|
|
69
|
+
* Kind constants for GCE resources
|
|
70
|
+
*/
|
|
71
|
+
const KINDS = {
|
|
72
|
+
INSTANCE: "gce:instance",
|
|
73
|
+
ACTION: "gce:action",
|
|
74
|
+
EXECUTABLE: "afs:executable",
|
|
75
|
+
NODE: "afs:node",
|
|
76
|
+
GCP_RESOURCE: "gcp:resource"
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
|
|
81
|
+
function __decorate(decorators, target, key, desc) {
|
|
82
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
83
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
84
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
85
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/index.ts
|
|
90
|
+
/**
|
|
91
|
+
* AFS GCE Provider
|
|
92
|
+
*
|
|
93
|
+
* GCE provider using AFSBaseProvider decorator routing pattern.
|
|
94
|
+
* Provides file-system-like access to Google Compute Engine instances.
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* Map GCE SDK errors to appropriate AFS errors
|
|
98
|
+
*/
|
|
99
|
+
function mapGCEError(error, path) {
|
|
100
|
+
if (error instanceof AFSNotFoundError || error instanceof AFSReadonlyError) return error;
|
|
101
|
+
if (typeof error === "object" && error !== null && "code" in error) {
|
|
102
|
+
const err = error;
|
|
103
|
+
if (err.code === 404) return new AFSNotFoundError(path ?? "/", err.message);
|
|
104
|
+
}
|
|
105
|
+
if (error instanceof Error) return error;
|
|
106
|
+
return new Error(String(error));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* AFSGCE Provider using AFSBaseProvider pattern
|
|
110
|
+
*
|
|
111
|
+
* Provides file-system-like access to GCE instances.
|
|
112
|
+
* Uses decorator routing (@List, @Read, @Write, @Delete, @Meta, @Actions, @Explain).
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const gce = new AFSGCE({
|
|
117
|
+
* projectId: "my-project",
|
|
118
|
+
* zone: "us-central1-a",
|
|
119
|
+
* });
|
|
120
|
+
*
|
|
121
|
+
* // Mount to AFS
|
|
122
|
+
* afs.mount(gce);
|
|
123
|
+
*
|
|
124
|
+
* // List instances
|
|
125
|
+
* const result = await afs.list("/modules/gce/instances");
|
|
126
|
+
*
|
|
127
|
+
* // Read instance metadata
|
|
128
|
+
* const meta = await afs.read("/modules/gce/instances/my-vm/metadata.json");
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
var AFSGCE = class AFSGCE extends AFSBaseProvider {
|
|
132
|
+
name;
|
|
133
|
+
description;
|
|
134
|
+
accessMode;
|
|
135
|
+
client;
|
|
136
|
+
projectId;
|
|
137
|
+
zone;
|
|
138
|
+
constructor(options) {
|
|
139
|
+
super();
|
|
140
|
+
const { uri: _uri, token: _token, auth: _auth, ...cleanOptions } = options;
|
|
141
|
+
const validated = afsgceOptionsSchema.parse(cleanOptions);
|
|
142
|
+
this.name = validated.name || "gce";
|
|
143
|
+
this.description = validated.description;
|
|
144
|
+
this.accessMode = validated.accessMode || "readonly";
|
|
145
|
+
this.projectId = validated.projectId;
|
|
146
|
+
this.zone = validated.zone;
|
|
147
|
+
this.client = new InstancesClient({
|
|
148
|
+
keyFilename: validated.keyFilename,
|
|
149
|
+
credentials: validated.credentials ? {
|
|
150
|
+
client_email: validated.credentials.clientEmail,
|
|
151
|
+
private_key: validated.credentials.privateKey
|
|
152
|
+
} : void 0
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
static schema() {
|
|
156
|
+
return afsgceOptionsSchema;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Provider manifest for URI-based discovery
|
|
160
|
+
*/
|
|
161
|
+
static manifest() {
|
|
162
|
+
return {
|
|
163
|
+
name: "gce",
|
|
164
|
+
description: "Google Compute Engine VM instances in a project/zone.\n- List and inspect instances, view metadata, labels, and network config\n- Exec actions: `start`, `stop`, `restart`, `delete`\n- Path structure: `/instances/{instance-name}`",
|
|
165
|
+
uriTemplate: "gce://{project}/{zone}",
|
|
166
|
+
category: "cloud-compute",
|
|
167
|
+
schema: z.object({
|
|
168
|
+
project: z.string(),
|
|
169
|
+
zone: z.string(),
|
|
170
|
+
keyFilename: z.string().optional()
|
|
171
|
+
}),
|
|
172
|
+
tags: [
|
|
173
|
+
"gcp",
|
|
174
|
+
"gce",
|
|
175
|
+
"cloud",
|
|
176
|
+
"compute"
|
|
177
|
+
]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
static async load({ basePath, config } = {}) {
|
|
181
|
+
return new AFSGCE(zodParse(afsgceOptionsSchema, config, { prefix: basePath }));
|
|
182
|
+
}
|
|
183
|
+
async getInstance(instanceName) {
|
|
184
|
+
try {
|
|
185
|
+
const [instance] = await this.client.get({
|
|
186
|
+
project: this.projectId,
|
|
187
|
+
zone: this.zone,
|
|
188
|
+
instance: instanceName
|
|
189
|
+
});
|
|
190
|
+
return instance;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
throw mapGCEError(error, joinURL("/instances", instanceName));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async listAllInstances(maxResults) {
|
|
196
|
+
const [instances] = await this.client.list({
|
|
197
|
+
project: this.projectId,
|
|
198
|
+
zone: this.zone,
|
|
199
|
+
maxResults: maxResults ?? 1e3
|
|
200
|
+
});
|
|
201
|
+
return instances || [];
|
|
202
|
+
}
|
|
203
|
+
buildInstanceEntry(instance, basePath) {
|
|
204
|
+
const name = instance.name;
|
|
205
|
+
const machineType = instance.machineType?.split("/").pop() || "unknown";
|
|
206
|
+
return {
|
|
207
|
+
id: `gce://${this.projectId}/${this.zone}/${name}`,
|
|
208
|
+
path: joinURL(basePath, name),
|
|
209
|
+
meta: {
|
|
210
|
+
kind: KINDS.INSTANCE,
|
|
211
|
+
kinds: [
|
|
212
|
+
KINDS.INSTANCE,
|
|
213
|
+
KINDS.GCP_RESOURCE,
|
|
214
|
+
KINDS.NODE
|
|
215
|
+
],
|
|
216
|
+
childrenCount: -1,
|
|
217
|
+
status: instance.status,
|
|
218
|
+
machineType,
|
|
219
|
+
privateIp: instance.networkInterfaces?.[0]?.networkIP,
|
|
220
|
+
publicIp: instance.networkInterfaces?.[0]?.accessConfigs?.[0]?.natIP,
|
|
221
|
+
createdAt: instance.creationTimestamp,
|
|
222
|
+
labels: instance.labels,
|
|
223
|
+
platformRef: generateInstancePlatformRef(this.projectId, this.zone, name)
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
async listRoot(_ctx) {
|
|
228
|
+
return { data: [{
|
|
229
|
+
id: `gce://${this.projectId}/instances`,
|
|
230
|
+
path: "/instances",
|
|
231
|
+
meta: {
|
|
232
|
+
kind: KINDS.NODE,
|
|
233
|
+
kinds: [KINDS.NODE],
|
|
234
|
+
childrenCount: -1,
|
|
235
|
+
description: "GCE Instances",
|
|
236
|
+
platformRef: generateInstancesListPlatformRef(this.projectId)
|
|
237
|
+
}
|
|
238
|
+
}, {
|
|
239
|
+
id: `gce://${this.projectId}/by-status`,
|
|
240
|
+
path: "/by-status",
|
|
241
|
+
meta: {
|
|
242
|
+
kind: KINDS.NODE,
|
|
243
|
+
kinds: [KINDS.NODE],
|
|
244
|
+
childrenCount: GCE_INSTANCE_STATES.length,
|
|
245
|
+
description: "Instances grouped by status"
|
|
246
|
+
}
|
|
247
|
+
}] };
|
|
248
|
+
}
|
|
249
|
+
async listInstances(_ctx) {
|
|
250
|
+
try {
|
|
251
|
+
return { data: (await this.listAllInstances()).map((instance) => this.buildInstanceEntry(instance, "/instances")) };
|
|
252
|
+
} catch (error) {
|
|
253
|
+
throw mapGCEError(error, "/instances");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async listInstanceChildren(ctx) {
|
|
257
|
+
await this.getInstance(ctx.params.instanceId);
|
|
258
|
+
return { data: [{
|
|
259
|
+
id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/metadata.json`,
|
|
260
|
+
path: joinURL(ctx.path, "metadata.json"),
|
|
261
|
+
meta: {
|
|
262
|
+
kind: KINDS.NODE,
|
|
263
|
+
kinds: [KINDS.NODE],
|
|
264
|
+
description: "Instance metadata"
|
|
265
|
+
}
|
|
266
|
+
}, {
|
|
267
|
+
id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/labels`,
|
|
268
|
+
path: joinURL(ctx.path, "labels"),
|
|
269
|
+
meta: {
|
|
270
|
+
kind: KINDS.NODE,
|
|
271
|
+
kinds: [KINDS.NODE],
|
|
272
|
+
childrenCount: -1,
|
|
273
|
+
description: "Instance labels"
|
|
274
|
+
}
|
|
275
|
+
}] };
|
|
276
|
+
}
|
|
277
|
+
async listInstanceLabels(ctx) {
|
|
278
|
+
const labels = (await this.getInstance(ctx.params.instanceId)).labels || {};
|
|
279
|
+
return { data: Object.entries(labels).map(([key, value]) => ({
|
|
280
|
+
id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/labels/${key}`,
|
|
281
|
+
path: joinURL(ctx.path, encodeURIComponent(key)),
|
|
282
|
+
content: value,
|
|
283
|
+
meta: { kind: "gce:label" }
|
|
284
|
+
})) };
|
|
285
|
+
}
|
|
286
|
+
async listByStatus(_ctx) {
|
|
287
|
+
return { data: GCE_INSTANCE_STATES.map((status) => ({
|
|
288
|
+
id: `gce://${this.projectId}/by-status/${status.toLowerCase()}`,
|
|
289
|
+
path: joinURL("/by-status", status.toLowerCase()),
|
|
290
|
+
meta: {
|
|
291
|
+
kind: KINDS.NODE,
|
|
292
|
+
kinds: [KINDS.NODE],
|
|
293
|
+
childrenCount: -1,
|
|
294
|
+
description: `Instances in ${status} state`
|
|
295
|
+
}
|
|
296
|
+
})) };
|
|
297
|
+
}
|
|
298
|
+
async listByStatusFilter(ctx) {
|
|
299
|
+
try {
|
|
300
|
+
const statusUpper = ctx.params.status.toUpperCase();
|
|
301
|
+
return { data: (await this.listAllInstances()).filter((inst) => inst.status?.toUpperCase() === statusUpper).map((instance) => this.buildInstanceEntry(instance, ctx.path)) };
|
|
302
|
+
} catch (error) {
|
|
303
|
+
throw mapGCEError(error, ctx.path);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async readCapabilities(_ctx) {
|
|
307
|
+
const actionCatalogs = [];
|
|
308
|
+
actionCatalogs.push({
|
|
309
|
+
kind: "gce:instance",
|
|
310
|
+
description: "Instance lifecycle operations",
|
|
311
|
+
catalog: [
|
|
312
|
+
{
|
|
313
|
+
name: "start",
|
|
314
|
+
description: "Start a stopped or terminated instance",
|
|
315
|
+
inputSchema: {
|
|
316
|
+
type: "object",
|
|
317
|
+
properties: {}
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: "stop",
|
|
322
|
+
description: "Stop a running instance",
|
|
323
|
+
inputSchema: {
|
|
324
|
+
type: "object",
|
|
325
|
+
properties: {}
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: "reset",
|
|
330
|
+
description: "Reset (hard reboot) a running instance",
|
|
331
|
+
inputSchema: {
|
|
332
|
+
type: "object",
|
|
333
|
+
properties: {}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
],
|
|
337
|
+
discovery: {
|
|
338
|
+
pathTemplate: "/instances/:instanceName/.actions",
|
|
339
|
+
note: "Replace :instanceName with actual instance name"
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
if (this.accessMode === "readwrite") actionCatalogs.push({
|
|
343
|
+
kind: "gce:global",
|
|
344
|
+
description: "Global GCE operations",
|
|
345
|
+
catalog: [{
|
|
346
|
+
name: "refresh",
|
|
347
|
+
description: "Refresh cached instance data",
|
|
348
|
+
inputSchema: {
|
|
349
|
+
type: "object",
|
|
350
|
+
properties: {}
|
|
351
|
+
}
|
|
352
|
+
}],
|
|
353
|
+
discovery: {
|
|
354
|
+
pathTemplate: "/.actions",
|
|
355
|
+
note: "Available at provider root"
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
return {
|
|
359
|
+
id: "/.meta/.capabilities",
|
|
360
|
+
path: "/.meta/.capabilities",
|
|
361
|
+
content: {
|
|
362
|
+
schemaVersion: 1,
|
|
363
|
+
provider: this.name,
|
|
364
|
+
version: "1.0.0",
|
|
365
|
+
description: this.description,
|
|
366
|
+
tools: [],
|
|
367
|
+
actions: actionCatalogs,
|
|
368
|
+
operations: this.getOperationsDeclaration()
|
|
369
|
+
},
|
|
370
|
+
meta: { kind: "afs:capabilities" }
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
async readInstanceMetadata(ctx) {
|
|
374
|
+
try {
|
|
375
|
+
const instance = await this.getInstance(ctx.params.instanceId);
|
|
376
|
+
return {
|
|
377
|
+
id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/metadata.json`,
|
|
378
|
+
path: ctx.path,
|
|
379
|
+
meta: {
|
|
380
|
+
kind: KINDS.NODE,
|
|
381
|
+
kinds: [KINDS.NODE],
|
|
382
|
+
description: "Instance metadata"
|
|
383
|
+
},
|
|
384
|
+
content: JSON.stringify(instance, null, 2)
|
|
385
|
+
};
|
|
386
|
+
} catch (error) {
|
|
387
|
+
throw mapGCEError(error, ctx.path);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
async readInstanceLabel(ctx) {
|
|
391
|
+
const value = ((await this.getInstance(ctx.params.instanceId)).labels || {})[ctx.params.labelKey];
|
|
392
|
+
if (value === void 0) throw new AFSNotFoundError(ctx.path);
|
|
393
|
+
return {
|
|
394
|
+
id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/labels/${ctx.params.labelKey}`,
|
|
395
|
+
path: ctx.path,
|
|
396
|
+
content: value,
|
|
397
|
+
meta: { kind: "gce:label" }
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
async metaRoot(ctx) {
|
|
401
|
+
return {
|
|
402
|
+
id: `gce://${this.projectId}/`,
|
|
403
|
+
path: ctx.path,
|
|
404
|
+
meta: {
|
|
405
|
+
kind: KINDS.NODE,
|
|
406
|
+
kinds: [KINDS.NODE],
|
|
407
|
+
childrenCount: 2,
|
|
408
|
+
description: "GCE Root"
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
async metaInstances(ctx) {
|
|
413
|
+
return {
|
|
414
|
+
id: `gce://${this.projectId}/instances`,
|
|
415
|
+
path: ctx.path,
|
|
416
|
+
meta: {
|
|
417
|
+
kind: KINDS.NODE,
|
|
418
|
+
kinds: [KINDS.NODE],
|
|
419
|
+
childrenCount: -1,
|
|
420
|
+
description: "GCE Instances",
|
|
421
|
+
platformRef: generateInstancesListPlatformRef(this.projectId)
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
async metaInstance(ctx) {
|
|
426
|
+
try {
|
|
427
|
+
const instance = await this.getInstance(ctx.params.instanceId);
|
|
428
|
+
const machineType = instance.machineType?.split("/").pop() || "unknown";
|
|
429
|
+
return {
|
|
430
|
+
id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}`,
|
|
431
|
+
path: ctx.path,
|
|
432
|
+
meta: {
|
|
433
|
+
kind: KINDS.INSTANCE,
|
|
434
|
+
kinds: [
|
|
435
|
+
KINDS.INSTANCE,
|
|
436
|
+
KINDS.GCP_RESOURCE,
|
|
437
|
+
KINDS.NODE
|
|
438
|
+
],
|
|
439
|
+
childrenCount: -1,
|
|
440
|
+
status: instance.status,
|
|
441
|
+
machineType,
|
|
442
|
+
privateIp: instance.networkInterfaces?.[0]?.networkIP,
|
|
443
|
+
publicIp: instance.networkInterfaces?.[0]?.accessConfigs?.[0]?.natIP,
|
|
444
|
+
createdAt: instance.creationTimestamp,
|
|
445
|
+
labels: instance.labels,
|
|
446
|
+
platformRef: generateInstancePlatformRef(this.projectId, this.zone, ctx.params.instanceId)
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
} catch (error) {
|
|
450
|
+
throw mapGCEError(error, ctx.path);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
async statRoot(ctx) {
|
|
454
|
+
const meta = await this.metaRoot(ctx);
|
|
455
|
+
return { data: {
|
|
456
|
+
id: meta.id,
|
|
457
|
+
path: ctx.path,
|
|
458
|
+
meta: meta.meta
|
|
459
|
+
} };
|
|
460
|
+
}
|
|
461
|
+
async statInstances(ctx) {
|
|
462
|
+
const meta = await this.metaInstances(ctx);
|
|
463
|
+
return { data: {
|
|
464
|
+
id: meta.id,
|
|
465
|
+
path: ctx.path,
|
|
466
|
+
meta: meta.meta
|
|
467
|
+
} };
|
|
468
|
+
}
|
|
469
|
+
async statInstance(ctx) {
|
|
470
|
+
const meta = await this.metaInstance(ctx);
|
|
471
|
+
return { data: {
|
|
472
|
+
id: meta.id,
|
|
473
|
+
path: ctx.path,
|
|
474
|
+
meta: meta.meta
|
|
475
|
+
} };
|
|
476
|
+
}
|
|
477
|
+
async writeInstanceLabel(ctx, payload) {
|
|
478
|
+
if (this.accessMode !== "readwrite") throw new AFSReadonlyError("Write operations require readwrite access mode");
|
|
479
|
+
try {
|
|
480
|
+
const instance = await this.getInstance(ctx.params.instanceId);
|
|
481
|
+
const labels = { ...instance.labels || {} };
|
|
482
|
+
labels[ctx.params.labelKey] = String(payload.content ?? "");
|
|
483
|
+
await this.client.setLabels({
|
|
484
|
+
project: this.projectId,
|
|
485
|
+
zone: this.zone,
|
|
486
|
+
instance: ctx.params.instanceId,
|
|
487
|
+
instancesSetLabelsRequestResource: {
|
|
488
|
+
labels,
|
|
489
|
+
labelFingerprint: instance.labelFingerprint
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
return {
|
|
493
|
+
data: {
|
|
494
|
+
id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/labels/${ctx.params.labelKey}`,
|
|
495
|
+
path: ctx.path,
|
|
496
|
+
content: payload.content,
|
|
497
|
+
meta: { kind: "gce:label" }
|
|
498
|
+
},
|
|
499
|
+
message: `Label '${ctx.params.labelKey}' set on instance '${ctx.params.instanceId}'`
|
|
500
|
+
};
|
|
501
|
+
} catch (error) {
|
|
502
|
+
throw mapGCEError(error, ctx.path);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
async deleteInstanceLabel(ctx) {
|
|
506
|
+
if (this.accessMode !== "readwrite") throw new AFSReadonlyError("Delete operations require readwrite access mode");
|
|
507
|
+
try {
|
|
508
|
+
const instance = await this.getInstance(ctx.params.instanceId);
|
|
509
|
+
const labels = { ...instance.labels || {} };
|
|
510
|
+
if (!(ctx.params.labelKey in labels)) throw new AFSNotFoundError(ctx.path, `Label '${ctx.params.labelKey}' not found on instance '${ctx.params.instanceId}'`);
|
|
511
|
+
delete labels[ctx.params.labelKey];
|
|
512
|
+
await this.client.setLabels({
|
|
513
|
+
project: this.projectId,
|
|
514
|
+
zone: this.zone,
|
|
515
|
+
instance: ctx.params.instanceId,
|
|
516
|
+
instancesSetLabelsRequestResource: {
|
|
517
|
+
labels,
|
|
518
|
+
labelFingerprint: instance.labelFingerprint
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
return { message: `Label '${ctx.params.labelKey}' removed from instance '${ctx.params.instanceId}'` };
|
|
522
|
+
} catch (error) {
|
|
523
|
+
if (error instanceof AFSNotFoundError) throw error;
|
|
524
|
+
throw mapGCEError(error, ctx.path);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async listGlobalActions(ctx) {
|
|
528
|
+
return { data: [{
|
|
529
|
+
id: "refresh",
|
|
530
|
+
path: joinURL(ctx.path, "refresh"),
|
|
531
|
+
summary: "Refresh instance cache",
|
|
532
|
+
meta: {
|
|
533
|
+
kind: "afs:executable",
|
|
534
|
+
kinds: ["afs:executable", "afs:node"],
|
|
535
|
+
inputSchema: {
|
|
536
|
+
type: "object",
|
|
537
|
+
properties: {}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}] };
|
|
541
|
+
}
|
|
542
|
+
async refreshAction(_ctx, _args) {
|
|
543
|
+
return {
|
|
544
|
+
success: true,
|
|
545
|
+
data: { message: "Cache refreshed" }
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
async listInstanceActions(ctx) {
|
|
549
|
+
const status = (await this.getInstance(ctx.params.instanceId)).status;
|
|
550
|
+
const actions = [];
|
|
551
|
+
switch (status) {
|
|
552
|
+
case "RUNNING":
|
|
553
|
+
actions.push({
|
|
554
|
+
name: "stop",
|
|
555
|
+
summary: "Stop the instance"
|
|
556
|
+
}, {
|
|
557
|
+
name: "reset",
|
|
558
|
+
summary: "Reset (hard reboot) the instance"
|
|
559
|
+
});
|
|
560
|
+
break;
|
|
561
|
+
case "STOPPED":
|
|
562
|
+
case "TERMINATED":
|
|
563
|
+
actions.push({
|
|
564
|
+
name: "start",
|
|
565
|
+
summary: "Start the instance"
|
|
566
|
+
});
|
|
567
|
+
break;
|
|
568
|
+
case "SUSPENDED":
|
|
569
|
+
actions.push({
|
|
570
|
+
name: "start",
|
|
571
|
+
summary: "Resume the instance"
|
|
572
|
+
});
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
return { data: actions.map((action) => ({
|
|
576
|
+
id: action.name,
|
|
577
|
+
path: joinURL(ctx.path, action.name),
|
|
578
|
+
summary: action.summary,
|
|
579
|
+
meta: {
|
|
580
|
+
kind: "afs:executable",
|
|
581
|
+
kinds: ["afs:executable", "afs:node"],
|
|
582
|
+
inputSchema: {
|
|
583
|
+
type: "object",
|
|
584
|
+
properties: {}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
})) };
|
|
588
|
+
}
|
|
589
|
+
async startInstanceAction(ctx, _args) {
|
|
590
|
+
try {
|
|
591
|
+
const status = (await this.getInstance(ctx.params.instanceId)).status;
|
|
592
|
+
if (status !== "STOPPED" && status !== "TERMINATED") return {
|
|
593
|
+
success: false,
|
|
594
|
+
error: {
|
|
595
|
+
code: "INVALID_STATE",
|
|
596
|
+
message: `Cannot start instance in ${status} state. Instance must be STOPPED or TERMINATED.`
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
const [operation] = await this.client.start({
|
|
600
|
+
project: this.projectId,
|
|
601
|
+
zone: this.zone,
|
|
602
|
+
instance: ctx.params.instanceId
|
|
603
|
+
});
|
|
604
|
+
return {
|
|
605
|
+
success: true,
|
|
606
|
+
data: { operationName: operation.latestResponse?.name || "unknown" }
|
|
607
|
+
};
|
|
608
|
+
} catch (error) {
|
|
609
|
+
throw mapGCEError(error, ctx.path);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async stopInstanceAction(ctx, _args) {
|
|
613
|
+
try {
|
|
614
|
+
const status = (await this.getInstance(ctx.params.instanceId)).status;
|
|
615
|
+
if (status !== "RUNNING") return {
|
|
616
|
+
success: false,
|
|
617
|
+
error: {
|
|
618
|
+
code: "INVALID_STATE",
|
|
619
|
+
message: `Cannot stop instance in ${status} state. Instance must be RUNNING.`
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
const [operation] = await this.client.stop({
|
|
623
|
+
project: this.projectId,
|
|
624
|
+
zone: this.zone,
|
|
625
|
+
instance: ctx.params.instanceId
|
|
626
|
+
});
|
|
627
|
+
return {
|
|
628
|
+
success: true,
|
|
629
|
+
data: { operationName: operation.latestResponse?.name || "unknown" }
|
|
630
|
+
};
|
|
631
|
+
} catch (error) {
|
|
632
|
+
throw mapGCEError(error, ctx.path);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
async resetInstanceAction(ctx, _args) {
|
|
636
|
+
try {
|
|
637
|
+
const status = (await this.getInstance(ctx.params.instanceId)).status;
|
|
638
|
+
if (status !== "RUNNING") return {
|
|
639
|
+
success: false,
|
|
640
|
+
error: {
|
|
641
|
+
code: "INVALID_STATE",
|
|
642
|
+
message: `Cannot reset instance in ${status} state. Instance must be RUNNING.`
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
const [operation] = await this.client.reset({
|
|
646
|
+
project: this.projectId,
|
|
647
|
+
zone: this.zone,
|
|
648
|
+
instance: ctx.params.instanceId
|
|
649
|
+
});
|
|
650
|
+
return {
|
|
651
|
+
success: true,
|
|
652
|
+
data: { operationName: operation.latestResponse?.name || "unknown" }
|
|
653
|
+
};
|
|
654
|
+
} catch (error) {
|
|
655
|
+
throw mapGCEError(error, ctx.path);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
async explainRoot(_ctx) {
|
|
659
|
+
try {
|
|
660
|
+
const instances = await this.listAllInstances();
|
|
661
|
+
const byStatus = {};
|
|
662
|
+
for (const inst of instances) {
|
|
663
|
+
const status = inst.status || "UNKNOWN";
|
|
664
|
+
byStatus[status] = (byStatus[status] || 0) + 1;
|
|
665
|
+
}
|
|
666
|
+
const statusLines = Object.entries(byStatus).map(([status, count]) => `- **${status}**: ${count}`).join("\n");
|
|
667
|
+
return {
|
|
668
|
+
format: "markdown",
|
|
669
|
+
content: `# GCE Provider
|
|
670
|
+
|
|
671
|
+
- **Project**: ${this.projectId}
|
|
672
|
+
- **Zone**: ${this.zone}
|
|
673
|
+
- **Access Mode**: ${this.accessMode}
|
|
674
|
+
- **Total Instances**: ${instances.length}
|
|
675
|
+
|
|
676
|
+
## Instances by Status
|
|
677
|
+
|
|
678
|
+
${statusLines || "- No instances found"}
|
|
679
|
+
|
|
680
|
+
## Navigation
|
|
681
|
+
|
|
682
|
+
- \`/instances/\` — list all instances
|
|
683
|
+
- \`/by-status/\` — instances grouped by status
|
|
684
|
+
- \`/.actions/\` — global actions
|
|
685
|
+
`
|
|
686
|
+
};
|
|
687
|
+
} catch (error) {
|
|
688
|
+
throw mapGCEError(error);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
async explainInstance(ctx) {
|
|
692
|
+
try {
|
|
693
|
+
const instance = await this.getInstance(ctx.params.instanceId);
|
|
694
|
+
const machineType = instance.machineType?.split("/").pop() || "unknown";
|
|
695
|
+
const privateIp = instance.networkInterfaces?.[0]?.networkIP || "none";
|
|
696
|
+
const publicIp = instance.networkInterfaces?.[0]?.accessConfigs?.[0]?.natIP || "none";
|
|
697
|
+
const labels = instance.labels || {};
|
|
698
|
+
const labelLines = Object.entries(labels).map(([k, v]) => `- \`${k}\`: ${v}`).join("\n");
|
|
699
|
+
return {
|
|
700
|
+
format: "markdown",
|
|
701
|
+
content: `# Instance: ${ctx.params.instanceId}
|
|
702
|
+
|
|
703
|
+
- **Status**: ${instance.status}
|
|
704
|
+
- **Machine Type**: ${machineType}
|
|
705
|
+
- **Zone**: ${this.zone}
|
|
706
|
+
- **Created**: ${instance.creationTimestamp || "unknown"}
|
|
707
|
+
|
|
708
|
+
## Network
|
|
709
|
+
|
|
710
|
+
- **Private IP**: ${privateIp}
|
|
711
|
+
- **Public IP**: ${publicIp}
|
|
712
|
+
|
|
713
|
+
## Labels
|
|
714
|
+
|
|
715
|
+
${labelLines || "- No labels"}
|
|
716
|
+
`
|
|
717
|
+
};
|
|
718
|
+
} catch (error) {
|
|
719
|
+
throw mapGCEError(error, ctx.path);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
__decorate([List("/")], AFSGCE.prototype, "listRoot", null);
|
|
724
|
+
__decorate([List("/instances")], AFSGCE.prototype, "listInstances", null);
|
|
725
|
+
__decorate([List("/instances/:instanceId"), List("/by-status/:status/:instanceId")], AFSGCE.prototype, "listInstanceChildren", null);
|
|
726
|
+
__decorate([List("/instances/:instanceId/labels"), List("/by-status/:status/:instanceId/labels")], AFSGCE.prototype, "listInstanceLabels", null);
|
|
727
|
+
__decorate([List("/by-status")], AFSGCE.prototype, "listByStatus", null);
|
|
728
|
+
__decorate([List("/by-status/:status")], AFSGCE.prototype, "listByStatusFilter", null);
|
|
729
|
+
__decorate([Read("/.meta/.capabilities")], AFSGCE.prototype, "readCapabilities", null);
|
|
730
|
+
__decorate([Read("/instances/:instanceId/metadata.json"), Read("/by-status/:status/:instanceId/metadata.json")], AFSGCE.prototype, "readInstanceMetadata", null);
|
|
731
|
+
__decorate([Read("/instances/:instanceId/labels/:labelKey"), Read("/by-status/:status/:instanceId/labels/:labelKey")], AFSGCE.prototype, "readInstanceLabel", null);
|
|
732
|
+
__decorate([Meta("/")], AFSGCE.prototype, "metaRoot", null);
|
|
733
|
+
__decorate([Meta("/instances")], AFSGCE.prototype, "metaInstances", null);
|
|
734
|
+
__decorate([Meta("/instances/:instanceId"), Meta("/by-status/:status/:instanceId")], AFSGCE.prototype, "metaInstance", null);
|
|
735
|
+
__decorate([Stat("/")], AFSGCE.prototype, "statRoot", null);
|
|
736
|
+
__decorate([Stat("/instances")], AFSGCE.prototype, "statInstances", null);
|
|
737
|
+
__decorate([Stat("/instances/:instanceId"), Stat("/by-status/:status/:instanceId")], AFSGCE.prototype, "statInstance", null);
|
|
738
|
+
__decorate([Write("/instances/:instanceId/labels/:labelKey"), Write("/by-status/:status/:instanceId/labels/:labelKey")], AFSGCE.prototype, "writeInstanceLabel", null);
|
|
739
|
+
__decorate([Delete("/instances/:instanceId/labels/:labelKey"), Delete("/by-status/:status/:instanceId/labels/:labelKey")], AFSGCE.prototype, "deleteInstanceLabel", null);
|
|
740
|
+
__decorate([Actions("/")], AFSGCE.prototype, "listGlobalActions", null);
|
|
741
|
+
__decorate([Actions.Exec("/", "refresh")], AFSGCE.prototype, "refreshAction", null);
|
|
742
|
+
__decorate([Actions("/instances/:instanceId"), Actions("/by-status/:status/:instanceId")], AFSGCE.prototype, "listInstanceActions", null);
|
|
743
|
+
__decorate([Actions.Exec("/instances/:instanceId", "start"), Actions.Exec("/by-status/:status/:instanceId", "start")], AFSGCE.prototype, "startInstanceAction", null);
|
|
744
|
+
__decorate([Actions.Exec("/instances/:instanceId", "stop"), Actions.Exec("/by-status/:status/:instanceId", "stop")], AFSGCE.prototype, "stopInstanceAction", null);
|
|
745
|
+
__decorate([Actions.Exec("/instances/:instanceId", "reset"), Actions.Exec("/by-status/:status/:instanceId", "reset")], AFSGCE.prototype, "resetInstanceAction", null);
|
|
746
|
+
__decorate([Explain("/")], AFSGCE.prototype, "explainRoot", null);
|
|
747
|
+
__decorate([Explain("/instances/:instanceId"), Explain("/by-status/:status/:instanceId")], AFSGCE.prototype, "explainInstance", null);
|
|
748
|
+
|
|
749
|
+
//#endregion
|
|
750
|
+
export { AFSGCE, afsgceOptionsSchema };
|
|
751
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/platform-ref.ts","../src/types.ts","../src/index.ts"],"sourcesContent":["/**\n * GCE Platform Reference\n *\n * Generates Google Cloud Console URLs for GCE resources.\n */\n\n/**\n * Platform reference containing console URL\n */\nexport interface GCEPlatformRef {\n consoleUrl: string;\n}\n\n/**\n * Generate platform reference with GCP Console URL for an instance\n *\n * @param projectId - GCP project ID\n * @param zone - GCE zone\n * @param instanceName - Instance name\n * @returns Platform reference with console URL\n */\nexport function generateInstancePlatformRef(\n projectId: string,\n zone: string,\n instanceName: string,\n): GCEPlatformRef {\n return {\n consoleUrl: `https://console.cloud.google.com/compute/instancesDetail/zones/${zone}/instances/${instanceName}?project=${projectId}`,\n };\n}\n\n/**\n * Generate platform reference with GCP Console URL for instances list\n *\n * @param projectId - GCP project ID\n * @returns Platform reference with console URL\n */\nexport function generateInstancesListPlatformRef(projectId: string): GCEPlatformRef {\n return {\n consoleUrl: `https://console.cloud.google.com/compute/instances?project=${projectId}`,\n };\n}\n","/**\n * AFS GCE Provider Types\n */\n\nimport { camelize, optionalize } from \"@aigne/afs/utils/zod\";\nimport { z } from \"zod\";\n\n/**\n * Configuration options for AFSGCE\n */\nexport interface AFSGCEOptions {\n /** Module name (used as mount path segment) */\n name?: string;\n\n /** Module description */\n description?: string;\n\n /** GCP project ID */\n projectId: string;\n\n /** GCE zone (e.g., us-central1-a) */\n zone: string;\n\n /** Access mode */\n accessMode?: \"readonly\" | \"readwrite\";\n\n /** Path to service account key file */\n keyFilename?: string;\n\n /** Explicit credentials (for programmatic access) */\n credentials?: {\n clientEmail: string;\n privateKey: string;\n };\n\n /** Cache TTL in seconds (0 = no cache) */\n cacheTtl?: number;\n}\n\n/**\n * GCE zone validation regex\n * Format: region-zone (e.g., us-central1-a, europe-west1-b)\n */\nconst ZONE_REGEX = /^[a-z]+-[a-z]+\\d+-[a-z]$/;\n\n/**\n * Zod schema for options validation\n */\nexport const afsgceOptionsSchema = camelize(\n z\n .object({\n name: optionalize(z.string()),\n description: optionalize(z.string()),\n projectId: z.string().min(1),\n zone: z.string().regex(ZONE_REGEX, \"Invalid GCE zone format (e.g., us-central1-a)\"),\n accessMode: optionalize(z.enum([\"readonly\", \"readwrite\"])),\n keyFilename: optionalize(z.string()),\n credentials: optionalize(\n z.object({\n clientEmail: z.string(),\n privateKey: z.string(),\n }),\n ),\n cacheTtl: optionalize(z.number().int().min(0)),\n })\n .strict(),\n);\n\n/**\n * GCE instance states\n */\nexport const GCE_INSTANCE_STATES = [\n \"PROVISIONING\",\n \"STAGING\",\n \"RUNNING\",\n \"STOPPING\",\n \"STOPPED\",\n \"SUSPENDING\",\n \"SUSPENDED\",\n \"TERMINATED\",\n] as const;\n\nexport type GCEInstanceState = (typeof GCE_INSTANCE_STATES)[number];\n\n/**\n * Path types for GCE resources\n */\nexport type PathType =\n | \"root\"\n | \"instances\"\n | \"instance\"\n | \"instance-metadata\"\n | \"instance-actions\"\n | \"instance-action\"\n | \"actions\"\n | \"global-action\"\n | \"unknown\";\n\nexport interface ParsedPath {\n type: PathType;\n instanceName?: string;\n action?: string;\n fileName?: string;\n raw: string;\n}\n\n/**\n * Kind constants for GCE resources\n */\nexport const KINDS = {\n INSTANCE: \"gce:instance\",\n ACTION: \"gce:action\",\n EXECUTABLE: \"afs:executable\",\n NODE: \"afs:node\",\n GCP_RESOURCE: \"gcp:resource\",\n} as const;\n","/**\n * AFS GCE Provider\n *\n * GCE provider using AFSBaseProvider decorator routing pattern.\n * Provides file-system-like access to Google Compute Engine instances.\n */\n\nimport {\n type ActionCatalog,\n type ActionDefinition,\n Actions,\n type AFSAccessMode,\n AFSBaseProvider,\n type AFSDeleteResult,\n type AFSEntry,\n type AFSExecResult,\n type AFSExplainResult,\n type AFSListResult,\n type AFSModuleClass,\n type AFSModuleLoadParams,\n AFSNotFoundError,\n AFSReadonlyError,\n type AFSStatResult,\n type AFSWriteEntryPayload,\n type AFSWriteResult,\n type CapabilitiesManifest,\n Delete,\n Explain,\n List,\n Meta,\n type ProviderManifest,\n Read,\n type RouteContext,\n Stat,\n Write,\n} from \"@aigne/afs\";\nimport { zodParse } from \"@aigne/afs/utils/zod\";\nimport { InstancesClient } from \"@google-cloud/compute\";\nimport { joinURL } from \"ufo\";\nimport { z } from \"zod\";\nimport { generateInstancePlatformRef, generateInstancesListPlatformRef } from \"./platform-ref.js\";\nimport {\n type AFSGCEOptions,\n afsgceOptionsSchema,\n GCE_INSTANCE_STATES,\n type GCEInstanceState,\n KINDS,\n} from \"./types.js\";\n\n/**\n * Map GCE SDK errors to appropriate AFS errors\n */\nfunction mapGCEError(error: unknown, path?: string): Error {\n if (error instanceof AFSNotFoundError || error instanceof AFSReadonlyError) {\n return error;\n }\n if (typeof error === \"object\" && error !== null && \"code\" in error) {\n const err = error as { code: number; message?: string };\n if (err.code === 404) {\n return new AFSNotFoundError(path ?? \"/\", err.message);\n }\n }\n if (error instanceof Error) {\n return error;\n }\n return new Error(String(error));\n}\n\n/**\n * AFSGCE Provider using AFSBaseProvider pattern\n *\n * Provides file-system-like access to GCE instances.\n * Uses decorator routing (@List, @Read, @Write, @Delete, @Meta, @Actions, @Explain).\n *\n * @example\n * ```typescript\n * const gce = new AFSGCE({\n * projectId: \"my-project\",\n * zone: \"us-central1-a\",\n * });\n *\n * // Mount to AFS\n * afs.mount(gce);\n *\n * // List instances\n * const result = await afs.list(\"/modules/gce/instances\");\n *\n * // Read instance metadata\n * const meta = await afs.read(\"/modules/gce/instances/my-vm/metadata.json\");\n * ```\n */\nexport class AFSGCE extends AFSBaseProvider {\n override readonly name: string;\n override readonly description?: string;\n override readonly accessMode: AFSAccessMode;\n\n private readonly client: InstancesClient;\n private readonly projectId: string;\n private readonly zone: string;\n\n constructor(options: AFSGCEOptions & { uri?: string; token?: string; auth?: unknown }) {\n super();\n\n // Strip registry-injected keys before strict schema validation\n const { uri: _uri, token: _token, auth: _auth, ...cleanOptions } = options as any;\n\n const validated = afsgceOptionsSchema.parse(cleanOptions);\n\n this.name = validated.name || \"gce\";\n this.description = validated.description;\n this.accessMode = validated.accessMode || \"readonly\";\n this.projectId = validated.projectId;\n this.zone = validated.zone;\n\n this.client = new InstancesClient({\n keyFilename: validated.keyFilename,\n credentials: validated.credentials\n ? {\n client_email: validated.credentials.clientEmail,\n private_key: validated.credentials.privateKey,\n }\n : undefined,\n });\n }\n\n static schema() {\n return afsgceOptionsSchema;\n }\n\n /**\n * Provider manifest for URI-based discovery\n */\n static manifest(): ProviderManifest {\n return {\n name: \"gce\",\n description:\n \"Google Compute Engine VM instances in a project/zone.\\n- List and inspect instances, view metadata, labels, and network config\\n- Exec actions: `start`, `stop`, `restart`, `delete`\\n- Path structure: `/instances/{instance-name}`\",\n uriTemplate: \"gce://{project}/{zone}\",\n category: \"cloud-compute\",\n schema: z.object({\n project: z.string(),\n zone: z.string(),\n keyFilename: z.string().optional(),\n }),\n tags: [\"gcp\", \"gce\", \"cloud\", \"compute\"],\n };\n }\n\n static async load({ basePath, config }: AFSModuleLoadParams = {}): Promise<AFSGCE> {\n const options = zodParse(afsgceOptionsSchema, config, { prefix: basePath });\n return new AFSGCE(options);\n }\n\n // ========== Helper Methods ==========\n\n private async getInstance(instanceName: string) {\n try {\n const [instance] = await this.client.get({\n project: this.projectId,\n zone: this.zone,\n instance: instanceName,\n });\n return instance;\n } catch (error) {\n throw mapGCEError(error, joinURL(\"/instances\", instanceName));\n }\n }\n\n private async listAllInstances(maxResults?: number) {\n const [instances] = await this.client.list({\n project: this.projectId,\n zone: this.zone,\n maxResults: maxResults ?? 1000,\n });\n return instances || [];\n }\n\n private buildInstanceEntry(instance: any, basePath: string): AFSEntry {\n const name = instance.name!;\n const machineType = instance.machineType?.split(\"/\").pop() || \"unknown\";\n\n return {\n id: `gce://${this.projectId}/${this.zone}/${name}`,\n path: joinURL(basePath, name),\n meta: {\n kind: KINDS.INSTANCE,\n kinds: [KINDS.INSTANCE, KINDS.GCP_RESOURCE, KINDS.NODE],\n childrenCount: -1,\n status: instance.status as GCEInstanceState,\n machineType,\n privateIp: instance.networkInterfaces?.[0]?.networkIP,\n publicIp: instance.networkInterfaces?.[0]?.accessConfigs?.[0]?.natIP,\n createdAt: instance.creationTimestamp,\n labels: instance.labels,\n platformRef: generateInstancePlatformRef(this.projectId, this.zone, name),\n },\n };\n }\n\n // ========== List Operations ==========\n\n @List(\"/\")\n async listRoot(_ctx: RouteContext): Promise<AFSListResult> {\n return {\n data: [\n {\n id: `gce://${this.projectId}/instances`,\n path: \"/instances\",\n meta: {\n kind: KINDS.NODE,\n kinds: [KINDS.NODE],\n childrenCount: -1,\n description: \"GCE Instances\",\n platformRef: generateInstancesListPlatformRef(this.projectId),\n },\n },\n {\n id: `gce://${this.projectId}/by-status`,\n path: \"/by-status\",\n meta: {\n kind: KINDS.NODE,\n kinds: [KINDS.NODE],\n childrenCount: GCE_INSTANCE_STATES.length,\n description: \"Instances grouped by status\",\n },\n },\n ],\n };\n }\n\n @List(\"/instances\")\n async listInstances(_ctx: RouteContext): Promise<AFSListResult> {\n try {\n const instances = await this.listAllInstances();\n const entries = instances.map((instance: any) =>\n this.buildInstanceEntry(instance, \"/instances\"),\n );\n return { data: entries };\n } catch (error) {\n throw mapGCEError(error, \"/instances\");\n }\n }\n\n @List(\"/instances/:instanceId\")\n @List(\"/by-status/:status/:instanceId\")\n async listInstanceChildren(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n ): Promise<AFSListResult> {\n await this.getInstance(ctx.params.instanceId);\n\n return {\n data: [\n {\n id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/metadata.json`,\n path: joinURL(ctx.path, \"metadata.json\"),\n meta: {\n kind: KINDS.NODE,\n kinds: [KINDS.NODE],\n description: \"Instance metadata\",\n },\n },\n {\n id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/labels`,\n path: joinURL(ctx.path, \"labels\"),\n meta: {\n kind: KINDS.NODE,\n kinds: [KINDS.NODE],\n childrenCount: -1,\n description: \"Instance labels\",\n },\n },\n ],\n };\n }\n\n @List(\"/instances/:instanceId/labels\")\n @List(\"/by-status/:status/:instanceId/labels\")\n async listInstanceLabels(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n ): Promise<AFSListResult> {\n const instance = await this.getInstance(ctx.params.instanceId);\n const labels = instance.labels || {};\n\n const entries: AFSEntry[] = Object.entries(labels).map(([key, value]) => ({\n id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/labels/${key}`,\n path: joinURL(ctx.path, encodeURIComponent(key)),\n content: value,\n meta: {\n kind: \"gce:label\",\n },\n }));\n\n return { data: entries };\n }\n\n @List(\"/by-status\")\n async listByStatus(_ctx: RouteContext): Promise<AFSListResult> {\n const entries: AFSEntry[] = GCE_INSTANCE_STATES.map((status) => ({\n id: `gce://${this.projectId}/by-status/${status.toLowerCase()}`,\n path: joinURL(\"/by-status\", status.toLowerCase()),\n meta: {\n kind: KINDS.NODE,\n kinds: [KINDS.NODE],\n childrenCount: -1,\n description: `Instances in ${status} state`,\n },\n }));\n\n return { data: entries };\n }\n\n @List(\"/by-status/:status\")\n async listByStatusFilter(ctx: RouteContext<{ status: string }>): Promise<AFSListResult> {\n try {\n const statusUpper = ctx.params.status.toUpperCase();\n const instances = await this.listAllInstances();\n const filtered = instances.filter(\n (inst: any) => (inst.status as string)?.toUpperCase() === statusUpper,\n );\n\n const entries = filtered.map((instance: any) => this.buildInstanceEntry(instance, ctx.path));\n\n return { data: entries };\n } catch (error) {\n throw mapGCEError(error, ctx.path);\n }\n }\n\n // ========== Read Operations ==========\n\n @Read(\"/.meta/.capabilities\")\n async readCapabilities(_ctx: RouteContext): Promise<AFSEntry> {\n const actionCatalogs: ActionCatalog[] = [];\n\n const instanceActions: ActionDefinition[] = [\n {\n name: \"start\",\n description: \"Start a stopped or terminated instance\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"stop\",\n description: \"Stop a running instance\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"reset\",\n description: \"Reset (hard reboot) a running instance\",\n inputSchema: { type: \"object\", properties: {} },\n },\n ];\n\n actionCatalogs.push({\n kind: \"gce:instance\",\n description: \"Instance lifecycle operations\",\n catalog: instanceActions,\n discovery: {\n pathTemplate: \"/instances/:instanceName/.actions\",\n note: \"Replace :instanceName with actual instance name\",\n },\n });\n\n if (this.accessMode === \"readwrite\") {\n actionCatalogs.push({\n kind: \"gce:global\",\n description: \"Global GCE operations\",\n catalog: [\n {\n name: \"refresh\",\n description: \"Refresh cached instance data\",\n inputSchema: { type: \"object\", properties: {} },\n },\n ],\n discovery: {\n pathTemplate: \"/.actions\",\n note: \"Available at provider root\",\n },\n });\n }\n\n const manifest: CapabilitiesManifest = {\n schemaVersion: 1,\n provider: this.name,\n version: \"1.0.0\",\n description: this.description,\n tools: [],\n actions: actionCatalogs,\n operations: this.getOperationsDeclaration(),\n };\n\n return {\n id: \"/.meta/.capabilities\",\n path: \"/.meta/.capabilities\",\n content: manifest,\n meta: { kind: \"afs:capabilities\" },\n };\n }\n\n @Read(\"/instances/:instanceId/metadata.json\")\n @Read(\"/by-status/:status/:instanceId/metadata.json\")\n async readInstanceMetadata(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n ): Promise<AFSEntry> {\n try {\n const instance = await this.getInstance(ctx.params.instanceId);\n\n return {\n id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/metadata.json`,\n path: ctx.path,\n meta: {\n kind: KINDS.NODE,\n kinds: [KINDS.NODE],\n description: \"Instance metadata\",\n },\n content: JSON.stringify(instance, null, 2),\n };\n } catch (error) {\n throw mapGCEError(error, ctx.path);\n }\n }\n\n @Read(\"/instances/:instanceId/labels/:labelKey\")\n @Read(\"/by-status/:status/:instanceId/labels/:labelKey\")\n async readInstanceLabel(\n ctx: RouteContext<{ instanceId: string; labelKey: string; status?: string }>,\n ): Promise<AFSEntry> {\n const instance = await this.getInstance(ctx.params.instanceId);\n const labels = instance.labels || {};\n const value = labels[ctx.params.labelKey];\n\n if (value === undefined) {\n throw new AFSNotFoundError(ctx.path);\n }\n\n return {\n id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/labels/${ctx.params.labelKey}`,\n path: ctx.path,\n content: value,\n meta: { kind: \"gce:label\" },\n };\n }\n\n // ========== Meta Operations ==========\n\n @Meta(\"/\")\n async metaRoot(ctx: RouteContext): Promise<AFSEntry> {\n return {\n id: `gce://${this.projectId}/`,\n path: ctx.path,\n meta: {\n kind: KINDS.NODE,\n kinds: [KINDS.NODE],\n childrenCount: 2,\n description: \"GCE Root\",\n },\n };\n }\n\n @Meta(\"/instances\")\n async metaInstances(ctx: RouteContext): Promise<AFSEntry> {\n return {\n id: `gce://${this.projectId}/instances`,\n path: ctx.path,\n meta: {\n kind: KINDS.NODE,\n kinds: [KINDS.NODE],\n childrenCount: -1,\n description: \"GCE Instances\",\n platformRef: generateInstancesListPlatformRef(this.projectId),\n },\n };\n }\n\n @Meta(\"/instances/:instanceId\")\n @Meta(\"/by-status/:status/:instanceId\")\n async metaInstance(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n ): Promise<AFSEntry> {\n try {\n const instance = await this.getInstance(ctx.params.instanceId);\n const machineType = instance.machineType?.split(\"/\").pop() || \"unknown\";\n\n return {\n id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}`,\n path: ctx.path,\n meta: {\n kind: KINDS.INSTANCE,\n kinds: [KINDS.INSTANCE, KINDS.GCP_RESOURCE, KINDS.NODE],\n childrenCount: -1,\n status: instance.status as GCEInstanceState,\n machineType,\n privateIp: instance.networkInterfaces?.[0]?.networkIP,\n publicIp: instance.networkInterfaces?.[0]?.accessConfigs?.[0]?.natIP,\n createdAt: instance.creationTimestamp,\n labels: instance.labels,\n platformRef: generateInstancePlatformRef(\n this.projectId,\n this.zone,\n ctx.params.instanceId,\n ),\n },\n };\n } catch (error) {\n throw mapGCEError(error, ctx.path);\n }\n }\n\n // ========== Stat Operations ==========\n\n @Stat(\"/\")\n async statRoot(ctx: RouteContext): Promise<AFSStatResult> {\n const meta = await this.metaRoot(ctx);\n return { data: { id: meta.id, path: ctx.path, meta: meta.meta as Record<string, unknown> } };\n }\n\n @Stat(\"/instances\")\n async statInstances(ctx: RouteContext): Promise<AFSStatResult> {\n const meta = await this.metaInstances(ctx);\n return { data: { id: meta.id, path: ctx.path, meta: meta.meta as Record<string, unknown> } };\n }\n\n @Stat(\"/instances/:instanceId\")\n @Stat(\"/by-status/:status/:instanceId\")\n async statInstance(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n ): Promise<AFSStatResult> {\n const meta = await this.metaInstance(ctx);\n return { data: { id: meta.id, path: ctx.path, meta: meta.meta as Record<string, unknown> } };\n }\n\n // ========== Write Operations ==========\n\n @Write(\"/instances/:instanceId/labels/:labelKey\")\n @Write(\"/by-status/:status/:instanceId/labels/:labelKey\")\n async writeInstanceLabel(\n ctx: RouteContext<{ instanceId: string; labelKey: string; status?: string }>,\n payload: AFSWriteEntryPayload,\n ): Promise<AFSWriteResult> {\n if (this.accessMode !== \"readwrite\") {\n throw new AFSReadonlyError(\"Write operations require readwrite access mode\");\n }\n\n try {\n const instance = await this.getInstance(ctx.params.instanceId);\n const labels = { ...(instance.labels || {}) };\n labels[ctx.params.labelKey] = String(payload.content ?? \"\");\n\n await this.client.setLabels({\n project: this.projectId,\n zone: this.zone,\n instance: ctx.params.instanceId,\n instancesSetLabelsRequestResource: {\n labels,\n labelFingerprint: instance.labelFingerprint,\n },\n });\n\n return {\n data: {\n id: `gce://${this.projectId}/${this.zone}/${ctx.params.instanceId}/labels/${ctx.params.labelKey}`,\n path: ctx.path,\n content: payload.content,\n meta: { kind: \"gce:label\" },\n },\n message: `Label '${ctx.params.labelKey}' set on instance '${ctx.params.instanceId}'`,\n };\n } catch (error) {\n throw mapGCEError(error, ctx.path);\n }\n }\n\n // ========== Delete Operations ==========\n\n @Delete(\"/instances/:instanceId/labels/:labelKey\")\n @Delete(\"/by-status/:status/:instanceId/labels/:labelKey\")\n async deleteInstanceLabel(\n ctx: RouteContext<{ instanceId: string; labelKey: string; status?: string }>,\n ): Promise<AFSDeleteResult> {\n if (this.accessMode !== \"readwrite\") {\n throw new AFSReadonlyError(\"Delete operations require readwrite access mode\");\n }\n\n try {\n const instance = await this.getInstance(ctx.params.instanceId);\n const labels = { ...(instance.labels || {}) };\n\n if (!(ctx.params.labelKey in labels)) {\n throw new AFSNotFoundError(\n ctx.path,\n `Label '${ctx.params.labelKey}' not found on instance '${ctx.params.instanceId}'`,\n );\n }\n\n delete labels[ctx.params.labelKey];\n\n await this.client.setLabels({\n project: this.projectId,\n zone: this.zone,\n instance: ctx.params.instanceId,\n instancesSetLabelsRequestResource: {\n labels,\n labelFingerprint: instance.labelFingerprint,\n },\n });\n\n return {\n message: `Label '${ctx.params.labelKey}' removed from instance '${ctx.params.instanceId}'`,\n };\n } catch (error) {\n if (error instanceof AFSNotFoundError) throw error;\n throw mapGCEError(error, ctx.path);\n }\n }\n\n // ========== Actions ==========\n\n @Actions(\"/\")\n async listGlobalActions(ctx: RouteContext): Promise<AFSListResult> {\n return {\n data: [\n {\n id: \"refresh\",\n path: joinURL(ctx.path, \"refresh\"),\n summary: \"Refresh instance cache\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: { type: \"object\", properties: {} },\n },\n },\n ],\n };\n }\n\n @Actions.Exec(\"/\", \"refresh\")\n async refreshAction(_ctx: RouteContext, _args: Record<string, unknown>): Promise<AFSExecResult> {\n return { success: true, data: { message: \"Cache refreshed\" } };\n }\n\n @Actions(\"/instances/:instanceId\")\n @Actions(\"/by-status/:status/:instanceId\")\n async listInstanceActions(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n ): Promise<AFSListResult> {\n const instance = await this.getInstance(ctx.params.instanceId);\n const status = instance.status as GCEInstanceState;\n\n const actions: Array<{ name: string; summary: string }> = [];\n\n switch (status) {\n case \"RUNNING\":\n actions.push(\n { name: \"stop\", summary: \"Stop the instance\" },\n { name: \"reset\", summary: \"Reset (hard reboot) the instance\" },\n );\n break;\n case \"STOPPED\":\n case \"TERMINATED\":\n actions.push({ name: \"start\", summary: \"Start the instance\" });\n break;\n case \"SUSPENDED\":\n actions.push({ name: \"start\", summary: \"Resume the instance\" });\n break;\n }\n\n return {\n data: actions.map((action) => ({\n id: action.name,\n path: joinURL(ctx.path, action.name),\n summary: action.summary,\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: { type: \"object\", properties: {} },\n },\n })),\n };\n }\n\n @Actions.Exec(\"/instances/:instanceId\", \"start\")\n @Actions.Exec(\"/by-status/:status/:instanceId\", \"start\")\n async startInstanceAction(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n _args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n try {\n const instance = await this.getInstance(ctx.params.instanceId);\n const status = instance.status as GCEInstanceState;\n\n if (status !== \"STOPPED\" && status !== \"TERMINATED\") {\n return {\n success: false,\n error: {\n code: \"INVALID_STATE\",\n message: `Cannot start instance in ${status} state. Instance must be STOPPED or TERMINATED.`,\n },\n };\n }\n\n const [operation] = await this.client.start({\n project: this.projectId,\n zone: this.zone,\n instance: ctx.params.instanceId,\n });\n\n return {\n success: true,\n data: { operationName: (operation as any).latestResponse?.name || \"unknown\" },\n };\n } catch (error) {\n throw mapGCEError(error, ctx.path);\n }\n }\n\n @Actions.Exec(\"/instances/:instanceId\", \"stop\")\n @Actions.Exec(\"/by-status/:status/:instanceId\", \"stop\")\n async stopInstanceAction(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n _args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n try {\n const instance = await this.getInstance(ctx.params.instanceId);\n const status = instance.status as GCEInstanceState;\n\n if (status !== \"RUNNING\") {\n return {\n success: false,\n error: {\n code: \"INVALID_STATE\",\n message: `Cannot stop instance in ${status} state. Instance must be RUNNING.`,\n },\n };\n }\n\n const [operation] = await this.client.stop({\n project: this.projectId,\n zone: this.zone,\n instance: ctx.params.instanceId,\n });\n\n return {\n success: true,\n data: { operationName: (operation as any).latestResponse?.name || \"unknown\" },\n };\n } catch (error) {\n throw mapGCEError(error, ctx.path);\n }\n }\n\n @Actions.Exec(\"/instances/:instanceId\", \"reset\")\n @Actions.Exec(\"/by-status/:status/:instanceId\", \"reset\")\n async resetInstanceAction(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n _args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n try {\n const instance = await this.getInstance(ctx.params.instanceId);\n const status = instance.status as GCEInstanceState;\n\n if (status !== \"RUNNING\") {\n return {\n success: false,\n error: {\n code: \"INVALID_STATE\",\n message: `Cannot reset instance in ${status} state. Instance must be RUNNING.`,\n },\n };\n }\n\n const [operation] = await this.client.reset({\n project: this.projectId,\n zone: this.zone,\n instance: ctx.params.instanceId,\n });\n\n return {\n success: true,\n data: { operationName: (operation as any).latestResponse?.name || \"unknown\" },\n };\n } catch (error) {\n throw mapGCEError(error, ctx.path);\n }\n }\n\n // ========== Explain ==========\n\n @Explain(\"/\")\n async explainRoot(_ctx: RouteContext): Promise<AFSExplainResult> {\n try {\n const instances = await this.listAllInstances();\n const byStatus: Record<string, number> = {};\n\n for (const inst of instances) {\n const status = (inst.status as string) || \"UNKNOWN\";\n byStatus[status] = (byStatus[status] || 0) + 1;\n }\n\n const statusLines = Object.entries(byStatus)\n .map(([status, count]) => `- **${status}**: ${count}`)\n .join(\"\\n\");\n\n const content = `# GCE Provider\n\n- **Project**: ${this.projectId}\n- **Zone**: ${this.zone}\n- **Access Mode**: ${this.accessMode}\n- **Total Instances**: ${instances.length}\n\n## Instances by Status\n\n${statusLines || \"- No instances found\"}\n\n## Navigation\n\n- \\`/instances/\\` — list all instances\n- \\`/by-status/\\` — instances grouped by status\n- \\`/.actions/\\` — global actions\n`;\n\n return { format: \"markdown\", content };\n } catch (error) {\n throw mapGCEError(error);\n }\n }\n\n @Explain(\"/instances/:instanceId\")\n @Explain(\"/by-status/:status/:instanceId\")\n async explainInstance(\n ctx: RouteContext<{ instanceId: string; status?: string }>,\n ): Promise<AFSExplainResult> {\n try {\n const instance = await this.getInstance(ctx.params.instanceId);\n const machineType = instance.machineType?.split(\"/\").pop() || \"unknown\";\n const privateIp = instance.networkInterfaces?.[0]?.networkIP || \"none\";\n const publicIp = instance.networkInterfaces?.[0]?.accessConfigs?.[0]?.natIP || \"none\";\n const labels = instance.labels || {};\n const labelLines = Object.entries(labels)\n .map(([k, v]) => `- \\`${k}\\`: ${v}`)\n .join(\"\\n\");\n\n const content = `# Instance: ${ctx.params.instanceId}\n\n- **Status**: ${instance.status}\n- **Machine Type**: ${machineType}\n- **Zone**: ${this.zone}\n- **Created**: ${instance.creationTimestamp || \"unknown\"}\n\n## Network\n\n- **Private IP**: ${privateIp}\n- **Public IP**: ${publicIp}\n\n## Labels\n\n${labelLines || \"- No labels\"}\n`;\n\n return { format: \"markdown\", content };\n } catch (error) {\n throw mapGCEError(error, ctx.path);\n }\n }\n}\n\n// Type check: AFSGCE should implement AFSModuleClass\nconst _typeCheck: AFSModuleClass<AFSGCE, AFSGCEOptions> = AFSGCE;\n\n// Re-export types\nexport type { AFSGCEOptions } from \"./types.js\";\nexport { afsgceOptionsSchema } from \"./types.js\";\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aigne/afs-gce",
|
|
3
|
+
"version": "1.11.0-beta.10",
|
|
4
|
+
"description": "AIGNE AFS module for Google Compute Engine resources",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"author": "Arcblock <blocklet@arcblock.io> https://github.com/arcblock",
|
|
10
|
+
"homepage": "https://github.com/arcblock/afs",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/arcblock/afs"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/arcblock/afs/issues"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "./dist/index.cjs",
|
|
20
|
+
"module": "./dist/index.mjs",
|
|
21
|
+
"types": "./dist/index.d.cts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"require": "./dist/index.cjs",
|
|
25
|
+
"import": "./dist/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./*": "./*"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"README.md",
|
|
33
|
+
"CHANGELOG.md"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@google-cloud/compute": "^4.10.0",
|
|
37
|
+
"ufo": "^1.6.3",
|
|
38
|
+
"zod": "^4.0.0",
|
|
39
|
+
"@aigne/afs": "^1.11.0-beta.10"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/bun": "^1.3.6",
|
|
43
|
+
"npm-run-all": "^4.1.5",
|
|
44
|
+
"rimraf": "^6.1.2",
|
|
45
|
+
"tsdown": "0.20.0-beta.3",
|
|
46
|
+
"typescript": "5.9.2",
|
|
47
|
+
"@aigne/typescript-config": "0.0.0",
|
|
48
|
+
"@aigne/scripts": "0.0.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsdown",
|
|
52
|
+
"check-types": "tsc --noEmit",
|
|
53
|
+
"clean": "rimraf dist coverage",
|
|
54
|
+
"test": "SKIP_E2E=1 bun test",
|
|
55
|
+
"test:e2e": "GOOGLE_APPLICATION_CREDENTIALS=${GOOGLE_APPLICATION_CREDENTIALS:-} bun test test/e2e.test.ts",
|
|
56
|
+
"test:all": "bun test",
|
|
57
|
+
"test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-reporter=text"
|
|
58
|
+
}
|
|
59
|
+
}
|