@shogo-ai/core 1.10.0 → 2.4.1

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,72 @@
1
+ // src/tech-stack-registry.ts
2
+ var TECH_STACK_REGISTRY = {
3
+ // Web (Vite-based) — share a bundled Vite template, so apps/api seeds them.
4
+ "react-app": { id: "react-app", target: "web" },
5
+ "threejs-game": { id: "threejs-game", target: "web" },
6
+ "phaser-game": { id: "phaser-game", target: "web" },
7
+ // Video (Remotion). `target: 'web'` because the canvas preview is the
8
+ // Remotion Player page the runtime serves from `dist/`; the stack's own
9
+ // `stack.json` declares `runtime.devServer: 'static-build'`, so no new
10
+ // target value (and therefore no new sizing/publish rules) is needed.
11
+ "video-editor": { id: "video-editor", target: "web", seedsOwnTemplate: true },
12
+ // Mobile (Metro/Expo) — agent-runtime owns the seed.
13
+ "expo-app": { id: "expo-app", target: "mobile", metro: true, seedsOwnTemplate: true },
14
+ "expo-three": { id: "expo-three", target: "mobile", metro: true, seedsOwnTemplate: true },
15
+ "react-native": { id: "react-native", target: "mobile", metro: true, seedsOwnTemplate: true },
16
+ // Data / scripting
17
+ "python-data": { id: "python-data", target: "data", seedsOwnTemplate: true },
18
+ // Multi-service backend (dockerd + docker compose). Requires the
19
+ // Docker-capable metal VM class; gated separately by
20
+ // `runtime.docker_class_enabled` (see apps/api/src/lib/runtime-class-setting.ts).
21
+ "docker-compose": {
22
+ id: "docker-compose",
23
+ target: "data",
24
+ seedsOwnTemplate: true,
25
+ vmClass: "docker",
26
+ minimumInstanceSize: "large",
27
+ ports: [
28
+ { port: 8e3, label: "app", protocol: "http", defaultVisibility: "preview" },
29
+ { port: 5432, label: "postgres", protocol: "tcp", defaultVisibility: "tunnel" }
30
+ ]
31
+ },
32
+ // Native (full game engines)
33
+ "unity-game": { id: "unity-game", target: "native", seedsOwnTemplate: true },
34
+ // Bring-your-own framework: the preview runs the project's static build.
35
+ custom: { id: "custom", target: "web", seedsOwnTemplate: true },
36
+ // Bare / no-stack
37
+ none: { id: "none", target: "none", seedsOwnTemplate: true }
38
+ };
39
+ function getStackEntry(techStackId) {
40
+ if (!techStackId) return null;
41
+ return TECH_STACK_REGISTRY[techStackId] ?? null;
42
+ }
43
+ function isMobileTechStack(techStackId) {
44
+ return getStackEntry(techStackId)?.target === "mobile";
45
+ }
46
+ function usesMetroBundler(techStackId) {
47
+ return getStackEntry(techStackId)?.metro === true;
48
+ }
49
+ function stackSeedsItself(techStackId) {
50
+ return getStackEntry(techStackId)?.seedsOwnTemplate === true;
51
+ }
52
+ function isDockerTechStack(techStackId) {
53
+ return getStackEntry(techStackId)?.vmClass === "docker";
54
+ }
55
+ function getMinimumInstanceSize(techStackId) {
56
+ return getStackEntry(techStackId)?.minimumInstanceSize ?? null;
57
+ }
58
+ function getDeclaredPorts(techStackId) {
59
+ return getStackEntry(techStackId)?.ports ?? [];
60
+ }
61
+
62
+ export {
63
+ TECH_STACK_REGISTRY,
64
+ getStackEntry,
65
+ isMobileTechStack,
66
+ usesMetroBundler,
67
+ stackSeedsItself,
68
+ isDockerTechStack,
69
+ getMinimumInstanceSize,
70
+ getDeclaredPorts
71
+ };
72
+ //# sourceMappingURL=chunk-NTAY4E43.js.map
@@ -18,6 +18,21 @@ function formatEntry(entry) {
18
18
  const extraStr = Object.keys(extra).length > 0 ? " " + JSON.stringify(extra) : "";
19
19
  return `[${service}] ${msg}${extraStr}`;
20
20
  }
21
+ var logSink = null;
22
+ function setOtelLogSink(sink) {
23
+ logSink = sink;
24
+ }
25
+ function emitLogToSink(entry) {
26
+ if (!logSink) return;
27
+ try {
28
+ logSink({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...entry });
29
+ } catch {
30
+ }
31
+ }
32
+ var traceContextProvider = null;
33
+ function setTraceContextProvider(provider) {
34
+ traceContextProvider = provider;
35
+ }
21
36
  function createLogger(service, defaultExtra) {
22
37
  function log(level, msg, extra) {
23
38
  if (!shouldLog(level)) return;
@@ -29,6 +44,16 @@ function createLogger(service, defaultExtra) {
29
44
  ...defaultExtra,
30
45
  ...extra
31
46
  };
47
+ if (traceContextProvider) {
48
+ try {
49
+ const tc = traceContextProvider();
50
+ if (tc) {
51
+ entry.trace_id = tc.trace_id;
52
+ entry.span_id = tc.span_id;
53
+ }
54
+ } catch {
55
+ }
56
+ }
32
57
  const formatted = formatEntry(entry);
33
58
  switch (level) {
34
59
  case "error":
@@ -41,6 +66,12 @@ function createLogger(service, defaultExtra) {
41
66
  console.log(formatted);
42
67
  break;
43
68
  }
69
+ if (logSink) {
70
+ try {
71
+ logSink(entry);
72
+ } catch {
73
+ }
74
+ }
44
75
  }
45
76
  return {
46
77
  debug: (msg, extra) => log("debug", msg, extra),
@@ -52,6 +83,9 @@ function createLogger(service, defaultExtra) {
52
83
  }
53
84
 
54
85
  export {
86
+ setOtelLogSink,
87
+ emitLogToSink,
88
+ setTraceContextProvider,
55
89
  createLogger
56
90
  };
57
- //# sourceMappingURL=chunk-24AL3V6R.js.map
91
+ //# sourceMappingURL=chunk-VW6ZGCBI.js.map
@@ -1,12 +1,27 @@
1
+ import {
2
+ setOtelLogSink,
3
+ setTraceContextProvider
4
+ } from "./chunk-VW6ZGCBI.js";
5
+
1
6
  // src/instrumentation.ts
2
- import { diag, DiagConsoleLogger, DiagLogLevel, trace, SpanStatusCode } from "@opentelemetry/api";
7
+ import { diag, DiagConsoleLogger, DiagLogLevel, trace, isSpanContextValid, SpanStatusCode } from "@opentelemetry/api";
8
+ import { logs, SeverityNumber } from "@opentelemetry/api-logs";
3
9
  import { NodeSDK } from "@opentelemetry/sdk-node";
4
10
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
11
+ import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
5
12
  import { BatchSpanProcessor, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
13
+ import { BatchLogRecordProcessor, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
6
14
  import { resourceFromAttributes } from "@opentelemetry/resources";
7
15
  import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
16
+ var SEVERITY_BY_LEVEL = {
17
+ debug: { number: SeverityNumber.DEBUG, text: "DEBUG" },
18
+ info: { number: SeverityNumber.INFO, text: "INFO" },
19
+ warn: { number: SeverityNumber.WARN, text: "WARN" },
20
+ error: { number: SeverityNumber.ERROR, text: "ERROR" }
21
+ };
8
22
  var sdk = null;
9
23
  function initInstrumentation(config) {
24
+ if (sdk) return;
10
25
  const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
11
26
  if (!endpoint) {
12
27
  console.log(`[OTEL] Tracing disabled for ${config.serviceName} (OTEL_EXPORTER_OTLP_ENDPOINT not set)`);
@@ -20,18 +35,43 @@ function initInstrumentation(config) {
20
35
  }
21
36
  const serviceName = process.env.OTEL_SERVICE_NAME || config.serviceName;
22
37
  console.log(`[OTEL] Configuring ${serviceName} \u2192 ${endpoint}/v1/traces`);
38
+ const exportTimeoutMs = Number(process.env.OTEL_EXPORTER_TIMEOUT_MS ?? 3e3);
23
39
  let traceExporter;
24
40
  try {
25
41
  traceExporter = new OTLPTraceExporter({
26
42
  url: `${endpoint}/v1/traces`,
27
- headers
43
+ headers,
44
+ timeoutMillis: exportTimeoutMs
28
45
  });
29
46
  } catch (err) {
30
47
  console.error(`[OTEL] Failed to create exporter: ${err.message}`);
31
48
  return;
32
49
  }
33
50
  const isProduction = process.env.NODE_ENV === "production" || process.env.NODE_ENV === "staging";
34
- const spanProcessor = isProduction ? new BatchSpanProcessor(traceExporter) : new SimpleSpanProcessor(traceExporter);
51
+ const spanProcessor = isProduction ? new BatchSpanProcessor(traceExporter, {
52
+ maxQueueSize: 2048,
53
+ maxExportBatchSize: 512,
54
+ scheduledDelayMillis: 5e3,
55
+ exportTimeoutMillis: exportTimeoutMs
56
+ }) : new SimpleSpanProcessor(traceExporter);
57
+ let logRecordProcessors = [];
58
+ try {
59
+ const logExporter = new OTLPLogExporter({
60
+ url: `${endpoint}/v1/logs`,
61
+ headers,
62
+ timeoutMillis: exportTimeoutMs
63
+ });
64
+ logRecordProcessors = [
65
+ isProduction ? new BatchLogRecordProcessor(logExporter, {
66
+ maxQueueSize: 2048,
67
+ maxExportBatchSize: 512,
68
+ scheduledDelayMillis: 5e3,
69
+ exportTimeoutMillis: exportTimeoutMs
70
+ }) : new SimpleLogRecordProcessor(logExporter)
71
+ ];
72
+ } catch (err) {
73
+ console.warn(`[OTEL] Failed to create log exporter for ${serviceName}: ${err.message}`);
74
+ }
35
75
  sdk = new NodeSDK({
36
76
  resource: resourceFromAttributes({
37
77
  [ATTR_SERVICE_NAME]: serviceName,
@@ -39,6 +79,7 @@ function initInstrumentation(config) {
39
79
  "deployment.environment.name": process.env.NODE_ENV || "development"
40
80
  }),
41
81
  spanProcessors: [spanProcessor],
82
+ logRecordProcessors,
42
83
  instrumentations: []
43
84
  });
44
85
  try {
@@ -49,6 +90,24 @@ function initInstrumentation(config) {
49
90
  sdk = null;
50
91
  return;
51
92
  }
93
+ setTraceContextProvider(() => {
94
+ const ctx = trace.getActiveSpan()?.spanContext();
95
+ return ctx && isSpanContextValid(ctx) ? { trace_id: ctx.traceId, span_id: ctx.spanId } : null;
96
+ });
97
+ if (logRecordProcessors.length > 0) {
98
+ const otelLogger = logs.getLogger(serviceName);
99
+ setOtelLogSink((entry) => {
100
+ const { level, msg, service, timestamp, ...attrs } = entry;
101
+ const severity = SEVERITY_BY_LEVEL[level] ?? SEVERITY_BY_LEVEL.info;
102
+ otelLogger.emit({
103
+ severityNumber: severity.number,
104
+ severityText: severity.text,
105
+ body: msg,
106
+ attributes: { service, ...attrs }
107
+ });
108
+ });
109
+ console.log(`[OTEL] Logs exporter enabled for ${serviceName} \u2192 ${endpoint}/v1/logs`);
110
+ }
52
111
  try {
53
112
  const tracer = trace.getTracer(serviceName);
54
113
  const testSpan = tracer.startSpan("otel.startup-test", {
@@ -69,13 +128,24 @@ function initInstrumentation(config) {
69
128
  }
70
129
  }
71
130
  async function shutdownInstrumentation() {
72
- if (sdk) {
73
- try {
74
- await sdk.shutdown();
75
- console.log("[OTEL] Tracing shut down");
76
- } catch (err) {
77
- console.error("[OTEL] Error shutting down:", err.message);
78
- }
131
+ if (!sdk) return;
132
+ setOtelLogSink(null);
133
+ setTraceContextProvider(null);
134
+ const shutdownTimeoutMs = Number(process.env.OTEL_SHUTDOWN_TIMEOUT_MS ?? 2e3);
135
+ let timer;
136
+ try {
137
+ await Promise.race([
138
+ sdk.shutdown(),
139
+ new Promise((resolve) => {
140
+ timer = setTimeout(resolve, shutdownTimeoutMs);
141
+ })
142
+ ]);
143
+ console.log("[OTEL] Tracing shut down");
144
+ } catch (err) {
145
+ console.error("[OTEL] Error shutting down:", err.message);
146
+ } finally {
147
+ if (timer) clearTimeout(timer);
148
+ sdk = null;
79
149
  }
80
150
  }
81
151
  function traceOperation(tracerName, spanName, attributes, fn) {
@@ -100,4 +170,4 @@ export {
100
170
  shutdownInstrumentation,
101
171
  traceOperation
102
172
  };
103
- //# sourceMappingURL=chunk-IDGVRGAL.js.map
173
+ //# sourceMappingURL=chunk-WRHTXHTH.js.map
@@ -20,7 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/file-types.ts
21
21
  var file_types_exports = {};
22
22
  __export(file_types_exports, {
23
+ BINARY_FILE_BASENAMES: () => BINARY_FILE_BASENAMES,
23
24
  BINARY_FILE_EXTENSIONS: () => BINARY_FILE_EXTENSIONS,
25
+ BINARY_FILE_TRANSIENT_SUFFIXES: () => BINARY_FILE_TRANSIENT_SUFFIXES,
26
+ isBinaryBuffer: () => isBinaryBuffer,
24
27
  isBinaryFilePath: () => isBinaryFilePath
25
28
  });
26
29
  module.exports = __toCommonJS(file_types_exports);
@@ -69,6 +72,24 @@ var BINARY_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
69
72
  "aac",
70
73
  "opus",
71
74
  "pdf",
75
+ // Office / document containers
76
+ "xls",
77
+ "xlsx",
78
+ "xlsm",
79
+ "xlsb",
80
+ "doc",
81
+ "docx",
82
+ "docm",
83
+ "ppt",
84
+ "pptx",
85
+ "pptm",
86
+ "odt",
87
+ "ods",
88
+ "odp",
89
+ "pages",
90
+ "numbers",
91
+ "key",
92
+ "epub",
72
93
  // Fonts
73
94
  "woff",
74
95
  "woff2",
@@ -136,16 +157,69 @@ var BINARY_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
136
157
  "nc",
137
158
  "tfrecord"
138
159
  ]);
139
- function isBinaryFilePath(path) {
140
- const dot = path.lastIndexOf(".");
141
- if (dot < 0) return false;
160
+ var BINARY_FILE_TRANSIENT_SUFFIXES = /* @__PURE__ */ new Set([
161
+ "part",
162
+ "partial",
163
+ "crdownload",
164
+ "download"
165
+ ]);
166
+ var BINARY_FILE_BASENAMES = /* @__PURE__ */ new Set([
167
+ "aapt",
168
+ "aapt2",
169
+ "adb",
170
+ "apksigner",
171
+ "bcc_compat",
172
+ "dexdump",
173
+ "dmtracedump",
174
+ "etc1tool",
175
+ "fastboot",
176
+ "hprof-conv",
177
+ "llvm-rs-cc",
178
+ "mksdcard",
179
+ "qemu-aarch64-static",
180
+ "qemu-arm-static",
181
+ "qemu-i386-static",
182
+ "qemu-x86_64-static",
183
+ "split-select",
184
+ "sqlite3",
185
+ "zipalign"
186
+ ]);
187
+ function basenameOf(path) {
142
188
  const slash = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
143
- if (dot < slash) return false;
144
- return BINARY_FILE_EXTENSIONS.has(path.slice(dot + 1).toLowerCase());
189
+ return path.slice(slash + 1);
190
+ }
191
+ function extensionOfBasename(basename) {
192
+ const dot = basename.lastIndexOf(".");
193
+ if (dot <= 0 || dot === basename.length - 1) return null;
194
+ return basename.slice(dot + 1).toLowerCase();
195
+ }
196
+ function isBinaryFilePath(path) {
197
+ let basename = basenameOf(path).toLowerCase();
198
+ if (BINARY_FILE_BASENAMES.has(basename)) return true;
199
+ for (let i = 0; i < 2; i++) {
200
+ const ext = extensionOfBasename(basename);
201
+ if (!ext) return false;
202
+ if (BINARY_FILE_EXTENSIONS.has(ext)) return true;
203
+ if (!BINARY_FILE_TRANSIENT_SUFFIXES.has(ext)) return false;
204
+ basename = basename.slice(0, -(ext.length + 1));
205
+ }
206
+ return false;
207
+ }
208
+ function isBinaryBuffer(bytes, inspectBytes = 8192) {
209
+ const head = bytes.subarray(0, Math.min(bytes.length, inspectBytes));
210
+ if (head.includes(0)) return true;
211
+ const startsWith = (...signature) => signature.every((value, index) => head[index] === value);
212
+ const ascii = (value) => Array.from(value).every(
213
+ (char, index) => head[index] === char.charCodeAt(0)
214
+ );
215
+ return startsWith(80, 75, 3, 4) || startsWith(80, 75, 5, 6) || startsWith(208, 207, 17, 224, 161, 177, 26, 225) || ascii("%PDF-") || startsWith(137, 80, 78, 71, 13, 10, 26, 10) || startsWith(255, 216, 255) || startsWith(71, 73, 70, 56) || startsWith(82, 73, 70, 70);
145
216
  }
146
217
  // Annotate the CommonJS export names for ESM import in node:
147
218
  0 && (module.exports = {
219
+ BINARY_FILE_BASENAMES,
148
220
  BINARY_FILE_EXTENSIONS,
221
+ BINARY_FILE_TRANSIENT_SUFFIXES,
222
+ isBinaryBuffer,
149
223
  isBinaryFilePath
150
224
  });
151
225
  //# sourceMappingURL=file-types.cjs.map
@@ -40,9 +40,28 @@
40
40
  * comment so the next person reading this file can tell at a glance
41
41
  * whether they need to add their own format. */
42
42
  declare const BINARY_FILE_EXTENSIONS: ReadonlySet<string>;
43
- /** True iff `path`'s extension is in {@link BINARY_FILE_EXTENSIONS}.
44
- * Case-insensitive; tolerant of paths with no extension (returns
45
- * false). Operates purely on the suffix — does not stat the file. */
43
+ /** Temporary/download suffixes commonly appended to the real filename while
44
+ * producers are still writing the file. `video.mp4.part` must inherit the
45
+ * binary classification from `video.mp4`; otherwise the IDE can route a
46
+ * still-mutating media file into Monaco as plaintext. */
47
+ declare const BINARY_FILE_TRANSIENT_SUFFIXES: ReadonlySet<string>;
48
+ /** Extensionless executables/binary tools that commonly appear in generated
49
+ * SDK/toolchain directories. These paths have no suffix to classify, but
50
+ * opening them in Monaco still corrupts the editor model/render lifecycle. */
51
+ declare const BINARY_FILE_BASENAMES: ReadonlySet<string>;
52
+ /** True iff `path` is known to represent binary content on disk.
53
+ * Case-insensitive; tolerant of paths with no extension. Also preserves
54
+ * binary classification through transient writer/download suffixes such as
55
+ * `.part` and blocks known extensionless binary tool names. Operates purely
56
+ * on the suffix/basename — does not stat the file. */
46
57
  declare function isBinaryFilePath(path: string): boolean;
58
+ /**
59
+ * Conservative content-level binary detection for files whose extension is
60
+ * missing, misleading, or not yet present in the allow-list. This is a
61
+ * secondary read-path guard; callers that write a file should still use
62
+ * isBinaryFilePath because a text-looking binary prefix is not sufficient to
63
+ * make a string round-trip safe.
64
+ */
65
+ declare function isBinaryBuffer(bytes: Uint8Array, inspectBytes?: number): boolean;
47
66
 
48
- export { BINARY_FILE_EXTENSIONS, isBinaryFilePath };
67
+ export { BINARY_FILE_BASENAMES, BINARY_FILE_EXTENSIONS, BINARY_FILE_TRANSIENT_SUFFIXES, isBinaryBuffer, isBinaryFilePath };
@@ -40,9 +40,28 @@
40
40
  * comment so the next person reading this file can tell at a glance
41
41
  * whether they need to add their own format. */
42
42
  declare const BINARY_FILE_EXTENSIONS: ReadonlySet<string>;
43
- /** True iff `path`'s extension is in {@link BINARY_FILE_EXTENSIONS}.
44
- * Case-insensitive; tolerant of paths with no extension (returns
45
- * false). Operates purely on the suffix — does not stat the file. */
43
+ /** Temporary/download suffixes commonly appended to the real filename while
44
+ * producers are still writing the file. `video.mp4.part` must inherit the
45
+ * binary classification from `video.mp4`; otherwise the IDE can route a
46
+ * still-mutating media file into Monaco as plaintext. */
47
+ declare const BINARY_FILE_TRANSIENT_SUFFIXES: ReadonlySet<string>;
48
+ /** Extensionless executables/binary tools that commonly appear in generated
49
+ * SDK/toolchain directories. These paths have no suffix to classify, but
50
+ * opening them in Monaco still corrupts the editor model/render lifecycle. */
51
+ declare const BINARY_FILE_BASENAMES: ReadonlySet<string>;
52
+ /** True iff `path` is known to represent binary content on disk.
53
+ * Case-insensitive; tolerant of paths with no extension. Also preserves
54
+ * binary classification through transient writer/download suffixes such as
55
+ * `.part` and blocks known extensionless binary tool names. Operates purely
56
+ * on the suffix/basename — does not stat the file. */
46
57
  declare function isBinaryFilePath(path: string): boolean;
58
+ /**
59
+ * Conservative content-level binary detection for files whose extension is
60
+ * missing, misleading, or not yet present in the allow-list. This is a
61
+ * secondary read-path guard; callers that write a file should still use
62
+ * isBinaryFilePath because a text-looking binary prefix is not sufficient to
63
+ * make a string round-trip safe.
64
+ */
65
+ declare function isBinaryBuffer(bytes: Uint8Array, inspectBytes?: number): boolean;
47
66
 
48
- export { BINARY_FILE_EXTENSIONS, isBinaryFilePath };
67
+ export { BINARY_FILE_BASENAMES, BINARY_FILE_EXTENSIONS, BINARY_FILE_TRANSIENT_SUFFIXES, isBinaryBuffer, isBinaryFilePath };
@@ -1,9 +1,15 @@
1
1
  import {
2
+ BINARY_FILE_BASENAMES,
2
3
  BINARY_FILE_EXTENSIONS,
4
+ BINARY_FILE_TRANSIENT_SUFFIXES,
5
+ isBinaryBuffer,
3
6
  isBinaryFilePath
4
- } from "./chunk-HKX3MEJN.js";
7
+ } from "./chunk-MDRCI2CN.js";
5
8
  export {
9
+ BINARY_FILE_BASENAMES,
6
10
  BINARY_FILE_EXTENSIONS,
11
+ BINARY_FILE_TRANSIENT_SUFFIXES,
12
+ isBinaryBuffer,
7
13
  isBinaryFilePath
8
14
  };
9
15
  //# sourceMappingURL=file-types.js.map