@shogo-ai/core 1.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.
- package/LICENSE +21 -0
- package/README.md +34 -0
- package/dist/chat-message.cjs +44 -0
- package/dist/chat-message.d.cts +20 -0
- package/dist/chat-message.d.ts +20 -0
- package/dist/chat-message.js +9 -0
- package/dist/chunk-24AL3V6R.js +57 -0
- package/dist/chunk-4J7NH2WJ.js +19 -0
- package/dist/chunk-ELMG6OXT.js +236 -0
- package/dist/chunk-IDGVRGAL.js +103 -0
- package/dist/chunk-KTDDFWXM.js +39 -0
- package/dist/chunk-VTD7O7YC.js +31 -0
- package/dist/index.cjs +510 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +45 -0
- package/dist/instrumentation.cjs +129 -0
- package/dist/instrumentation.d.cts +43 -0
- package/dist/instrumentation.d.ts +43 -0
- package/dist/instrumentation.js +11 -0
- package/dist/logger.cjs +81 -0
- package/dist/logger.d.cts +32 -0
- package/dist/logger.d.ts +32 -0
- package/dist/logger.js +7 -0
- package/dist/macos-junk.cjs +56 -0
- package/dist/macos-junk.d.cts +20 -0
- package/dist/macos-junk.d.ts +20 -0
- package/dist/macos-junk.js +9 -0
- package/dist/stream-buffer.cjs +261 -0
- package/dist/stream-buffer.d.cts +103 -0
- package/dist/stream-buffer.d.ts +103 -0
- package/dist/stream-buffer.js +9 -0
- package/dist/tech-stack-registry.cjs +67 -0
- package/dist/tech-stack-registry.d.cts +88 -0
- package/dist/tech-stack-registry.d.ts +88 -0
- package/dist/tech-stack-registry.js +15 -0
- package/package.json +118 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory stream buffer + turn ledger for SSE reconnect support.
|
|
3
|
+
*
|
|
4
|
+
* Stores raw SSE bytes keyed by an arbitrary string (typically a
|
|
5
|
+
* `chatSessionId`) so that a reconnecting client can replay buffered
|
|
6
|
+
* events and continue receiving live events. Every buffered chunk is
|
|
7
|
+
* tagged with a monotonically increasing `seq` so a client can resume
|
|
8
|
+
* from the last frame it has already seen via `?fromSeq=N` style
|
|
9
|
+
* resume.
|
|
10
|
+
*
|
|
11
|
+
* Each server process (agent-runtime pod, API server, custom user
|
|
12
|
+
* service) creates its own `StreamBufferStore` singleton — no shared
|
|
13
|
+
* state between processes. For cross-process durability a higher
|
|
14
|
+
* layer (e.g. a Postgres-backed turn ledger) would wrap this store;
|
|
15
|
+
* this in-memory variant is the hot cache.
|
|
16
|
+
*
|
|
17
|
+
* Lifted into the SDK from `@shogo/shared-runtime` (was AGPL) under MIT.
|
|
18
|
+
*/
|
|
19
|
+
type TurnStatus = 'active' | 'completed' | 'aborted' | 'failed';
|
|
20
|
+
interface TurnTerminal {
|
|
21
|
+
reason?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
interface StreamBufferWriter {
|
|
25
|
+
/** Append a raw byte chunk and return the seq assigned to it. */
|
|
26
|
+
append(chunk: Uint8Array): number;
|
|
27
|
+
/** Mark the turn as cleanly completed. Optional terminal reason. */
|
|
28
|
+
complete(reason?: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Mark the turn as failed (still keeps the buffer around for the grace
|
|
31
|
+
* window so a reconnecting client can see the terminal state).
|
|
32
|
+
*/
|
|
33
|
+
fail(error: string): void;
|
|
34
|
+
/** The turnId this writer is bound to. */
|
|
35
|
+
readonly turnId: string;
|
|
36
|
+
/** Last seq written so far (0 if nothing has been appended). */
|
|
37
|
+
readonly lastSeq: number;
|
|
38
|
+
}
|
|
39
|
+
interface ReplayOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Skip frames with seq <= fromSeq. Use 0 (or omit) to replay from the start.
|
|
42
|
+
*/
|
|
43
|
+
fromSeq?: number;
|
|
44
|
+
}
|
|
45
|
+
interface TurnSnapshot {
|
|
46
|
+
turnId: string;
|
|
47
|
+
status: TurnStatus;
|
|
48
|
+
lastSeq: number;
|
|
49
|
+
terminal: TurnTerminal | null;
|
|
50
|
+
createdAt: number;
|
|
51
|
+
completedAt: number | null;
|
|
52
|
+
lastEventAt: number;
|
|
53
|
+
}
|
|
54
|
+
declare class StreamBufferStore {
|
|
55
|
+
private buffers;
|
|
56
|
+
private cleanupTimer;
|
|
57
|
+
constructor();
|
|
58
|
+
/**
|
|
59
|
+
* Create (or replace) a buffer for the given key. Any existing active
|
|
60
|
+
* buffer is completed and discarded. Returns a writer bound to this
|
|
61
|
+
* specific buffer instance, including the new `turnId`.
|
|
62
|
+
*/
|
|
63
|
+
create(key: string, opts?: {
|
|
64
|
+
turnId?: string;
|
|
65
|
+
}): StreamBufferWriter;
|
|
66
|
+
/**
|
|
67
|
+
* Append a chunk by key (loose, untyped writer). Returns the seq assigned,
|
|
68
|
+
* or -1 if the buffer is unknown/inactive.
|
|
69
|
+
*/
|
|
70
|
+
append(key: string, chunk: Uint8Array): number;
|
|
71
|
+
/** Mark a buffer as completed by key. Optional terminal reason. */
|
|
72
|
+
complete(key: string, reason?: string): void;
|
|
73
|
+
/**
|
|
74
|
+
* Abort a stream: complete subscribers and remove the buffer entirely.
|
|
75
|
+
* Future resume/replay requests for this key will return null (→ 204).
|
|
76
|
+
*/
|
|
77
|
+
abort(key: string): void;
|
|
78
|
+
has(key: string): boolean;
|
|
79
|
+
/** Read-only metadata about a buffered turn. Useful for resume responses. */
|
|
80
|
+
snapshot(key: string): TurnSnapshot | null;
|
|
81
|
+
/**
|
|
82
|
+
* Create a ReadableStream that first replays all buffered chunks past
|
|
83
|
+
* `opts.fromSeq` (default: 0 → replay everything), then subscribes for
|
|
84
|
+
* any further live chunks until the stream completes.
|
|
85
|
+
*
|
|
86
|
+
* Returns null if no buffer exists for the key.
|
|
87
|
+
*/
|
|
88
|
+
createReplayStream(key: string, opts?: ReplayOptions): ReadableStream<Uint8Array> | null;
|
|
89
|
+
cleanup(): void;
|
|
90
|
+
dispose(): void;
|
|
91
|
+
private completeBuffer;
|
|
92
|
+
private failBuffer;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* TransformStream that copies every chunk into a StreamBufferStore while
|
|
96
|
+
* passing it through unchanged. On flush/cancel, completes the buffer.
|
|
97
|
+
*
|
|
98
|
+
* Note: this convenience helper does NOT propagate seq numbers back to the
|
|
99
|
+
* caller. Use `store.create(key)` directly if you need the writer's seq.
|
|
100
|
+
*/
|
|
101
|
+
declare function createBufferingTransform(store: StreamBufferStore, key: string): TransformStream<Uint8Array, Uint8Array>;
|
|
102
|
+
|
|
103
|
+
export { type ReplayOptions, StreamBufferStore, type StreamBufferWriter, type TurnSnapshot, type TurnStatus, type TurnTerminal, createBufferingTransform };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory stream buffer + turn ledger for SSE reconnect support.
|
|
3
|
+
*
|
|
4
|
+
* Stores raw SSE bytes keyed by an arbitrary string (typically a
|
|
5
|
+
* `chatSessionId`) so that a reconnecting client can replay buffered
|
|
6
|
+
* events and continue receiving live events. Every buffered chunk is
|
|
7
|
+
* tagged with a monotonically increasing `seq` so a client can resume
|
|
8
|
+
* from the last frame it has already seen via `?fromSeq=N` style
|
|
9
|
+
* resume.
|
|
10
|
+
*
|
|
11
|
+
* Each server process (agent-runtime pod, API server, custom user
|
|
12
|
+
* service) creates its own `StreamBufferStore` singleton — no shared
|
|
13
|
+
* state between processes. For cross-process durability a higher
|
|
14
|
+
* layer (e.g. a Postgres-backed turn ledger) would wrap this store;
|
|
15
|
+
* this in-memory variant is the hot cache.
|
|
16
|
+
*
|
|
17
|
+
* Lifted into the SDK from `@shogo/shared-runtime` (was AGPL) under MIT.
|
|
18
|
+
*/
|
|
19
|
+
type TurnStatus = 'active' | 'completed' | 'aborted' | 'failed';
|
|
20
|
+
interface TurnTerminal {
|
|
21
|
+
reason?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
interface StreamBufferWriter {
|
|
25
|
+
/** Append a raw byte chunk and return the seq assigned to it. */
|
|
26
|
+
append(chunk: Uint8Array): number;
|
|
27
|
+
/** Mark the turn as cleanly completed. Optional terminal reason. */
|
|
28
|
+
complete(reason?: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Mark the turn as failed (still keeps the buffer around for the grace
|
|
31
|
+
* window so a reconnecting client can see the terminal state).
|
|
32
|
+
*/
|
|
33
|
+
fail(error: string): void;
|
|
34
|
+
/** The turnId this writer is bound to. */
|
|
35
|
+
readonly turnId: string;
|
|
36
|
+
/** Last seq written so far (0 if nothing has been appended). */
|
|
37
|
+
readonly lastSeq: number;
|
|
38
|
+
}
|
|
39
|
+
interface ReplayOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Skip frames with seq <= fromSeq. Use 0 (or omit) to replay from the start.
|
|
42
|
+
*/
|
|
43
|
+
fromSeq?: number;
|
|
44
|
+
}
|
|
45
|
+
interface TurnSnapshot {
|
|
46
|
+
turnId: string;
|
|
47
|
+
status: TurnStatus;
|
|
48
|
+
lastSeq: number;
|
|
49
|
+
terminal: TurnTerminal | null;
|
|
50
|
+
createdAt: number;
|
|
51
|
+
completedAt: number | null;
|
|
52
|
+
lastEventAt: number;
|
|
53
|
+
}
|
|
54
|
+
declare class StreamBufferStore {
|
|
55
|
+
private buffers;
|
|
56
|
+
private cleanupTimer;
|
|
57
|
+
constructor();
|
|
58
|
+
/**
|
|
59
|
+
* Create (or replace) a buffer for the given key. Any existing active
|
|
60
|
+
* buffer is completed and discarded. Returns a writer bound to this
|
|
61
|
+
* specific buffer instance, including the new `turnId`.
|
|
62
|
+
*/
|
|
63
|
+
create(key: string, opts?: {
|
|
64
|
+
turnId?: string;
|
|
65
|
+
}): StreamBufferWriter;
|
|
66
|
+
/**
|
|
67
|
+
* Append a chunk by key (loose, untyped writer). Returns the seq assigned,
|
|
68
|
+
* or -1 if the buffer is unknown/inactive.
|
|
69
|
+
*/
|
|
70
|
+
append(key: string, chunk: Uint8Array): number;
|
|
71
|
+
/** Mark a buffer as completed by key. Optional terminal reason. */
|
|
72
|
+
complete(key: string, reason?: string): void;
|
|
73
|
+
/**
|
|
74
|
+
* Abort a stream: complete subscribers and remove the buffer entirely.
|
|
75
|
+
* Future resume/replay requests for this key will return null (→ 204).
|
|
76
|
+
*/
|
|
77
|
+
abort(key: string): void;
|
|
78
|
+
has(key: string): boolean;
|
|
79
|
+
/** Read-only metadata about a buffered turn. Useful for resume responses. */
|
|
80
|
+
snapshot(key: string): TurnSnapshot | null;
|
|
81
|
+
/**
|
|
82
|
+
* Create a ReadableStream that first replays all buffered chunks past
|
|
83
|
+
* `opts.fromSeq` (default: 0 → replay everything), then subscribes for
|
|
84
|
+
* any further live chunks until the stream completes.
|
|
85
|
+
*
|
|
86
|
+
* Returns null if no buffer exists for the key.
|
|
87
|
+
*/
|
|
88
|
+
createReplayStream(key: string, opts?: ReplayOptions): ReadableStream<Uint8Array> | null;
|
|
89
|
+
cleanup(): void;
|
|
90
|
+
dispose(): void;
|
|
91
|
+
private completeBuffer;
|
|
92
|
+
private failBuffer;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* TransformStream that copies every chunk into a StreamBufferStore while
|
|
96
|
+
* passing it through unchanged. On flush/cancel, completes the buffer.
|
|
97
|
+
*
|
|
98
|
+
* Note: this convenience helper does NOT propagate seq numbers back to the
|
|
99
|
+
* caller. Use `store.create(key)` directly if you need the writer's seq.
|
|
100
|
+
*/
|
|
101
|
+
declare function createBufferingTransform(store: StreamBufferStore, key: string): TransformStream<Uint8Array, Uint8Array>;
|
|
102
|
+
|
|
103
|
+
export { type ReplayOptions, StreamBufferStore, type StreamBufferWriter, type TurnSnapshot, type TurnStatus, type TurnTerminal, createBufferingTransform };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/tech-stack-registry.ts
|
|
21
|
+
var tech_stack_registry_exports = {};
|
|
22
|
+
__export(tech_stack_registry_exports, {
|
|
23
|
+
TECH_STACK_REGISTRY: () => TECH_STACK_REGISTRY,
|
|
24
|
+
getStackEntry: () => getStackEntry,
|
|
25
|
+
isMobileTechStack: () => isMobileTechStack,
|
|
26
|
+
stackSeedsItself: () => stackSeedsItself,
|
|
27
|
+
usesMetroBundler: () => usesMetroBundler
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(tech_stack_registry_exports);
|
|
30
|
+
var TECH_STACK_REGISTRY = {
|
|
31
|
+
// Web (Vite-based) — share a bundled Vite template, so apps/api seeds them.
|
|
32
|
+
"react-app": { id: "react-app", target: "web" },
|
|
33
|
+
"threejs-game": { id: "threejs-game", target: "web" },
|
|
34
|
+
"phaser-game": { id: "phaser-game", target: "web" },
|
|
35
|
+
// Mobile (Metro/Expo) — agent-runtime owns the seed.
|
|
36
|
+
"expo-app": { id: "expo-app", target: "mobile", metro: true, seedsOwnTemplate: true },
|
|
37
|
+
"expo-three": { id: "expo-three", target: "mobile", metro: true, seedsOwnTemplate: true },
|
|
38
|
+
"react-native": { id: "react-native", target: "mobile", metro: true, seedsOwnTemplate: true },
|
|
39
|
+
// Data / scripting
|
|
40
|
+
"python-data": { id: "python-data", target: "data", seedsOwnTemplate: true },
|
|
41
|
+
// Native (full game engines)
|
|
42
|
+
"unity-game": { id: "unity-game", target: "native", seedsOwnTemplate: true },
|
|
43
|
+
// Bare / no-stack
|
|
44
|
+
none: { id: "none", target: "none", seedsOwnTemplate: true }
|
|
45
|
+
};
|
|
46
|
+
function getStackEntry(techStackId) {
|
|
47
|
+
if (!techStackId) return null;
|
|
48
|
+
return TECH_STACK_REGISTRY[techStackId] ?? null;
|
|
49
|
+
}
|
|
50
|
+
function isMobileTechStack(techStackId) {
|
|
51
|
+
return getStackEntry(techStackId)?.target === "mobile";
|
|
52
|
+
}
|
|
53
|
+
function usesMetroBundler(techStackId) {
|
|
54
|
+
return getStackEntry(techStackId)?.metro === true;
|
|
55
|
+
}
|
|
56
|
+
function stackSeedsItself(techStackId) {
|
|
57
|
+
return getStackEntry(techStackId)?.seedsOwnTemplate === true;
|
|
58
|
+
}
|
|
59
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
60
|
+
0 && (module.exports = {
|
|
61
|
+
TECH_STACK_REGISTRY,
|
|
62
|
+
getStackEntry,
|
|
63
|
+
isMobileTechStack,
|
|
64
|
+
stackSeedsItself,
|
|
65
|
+
usesMetroBundler
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=tech-stack-registry.cjs.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tech-Stack Registry
|
|
3
|
+
* --------------------------------------------------------------------------
|
|
4
|
+
* Single source of truth for "what kind of project is this?" answered at
|
|
5
|
+
* runtime by the API and the agent-runtime.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists: we used to do `techStackId.startsWith('expo')` to decide
|
|
8
|
+
* whether a project was mobile. That heuristic broke the moment we added a
|
|
9
|
+
* stack like `expo-cli-tools` (would falsely match) and would silently miss
|
|
10
|
+
* a future stack like `react-native-bare` or `flutter` (would falsely not
|
|
11
|
+
* match).
|
|
12
|
+
*
|
|
13
|
+
* The replacement is an explicit, typed map keyed by stack id, with a
|
|
14
|
+
* `target` field that captures the platform the stack actually runs on.
|
|
15
|
+
* `apps/api` reads it to size pods correctly; the agent-runtime mirrors
|
|
16
|
+
* the same metadata in each stack's `stack.json` so the registry and the
|
|
17
|
+
* stack files stay in sync.
|
|
18
|
+
*
|
|
19
|
+
* Adding a new stack:
|
|
20
|
+
* 1. Add an entry below with `target: 'mobile' | 'web' | 'data' | 'native'`.
|
|
21
|
+
* 2. Add the matching `target` field to the stack's `stack.json`.
|
|
22
|
+
* 3. The runtime/API automatically pick up the new stack's class —
|
|
23
|
+
* no `if (id.startsWith(…))` to update.
|
|
24
|
+
*
|
|
25
|
+
* Lifted into the SDK from `@shogo/shared-runtime` (was AGPL) under MIT.
|
|
26
|
+
* The original `@shogo/shared-runtime/tech-stack-registry` re-exports this
|
|
27
|
+
* module unchanged for backwards compatibility.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Platform a stack ultimately runs on. Drives:
|
|
31
|
+
* - instance-size floors (mobile needs `small`+ for Metro/RN node_modules)
|
|
32
|
+
* - disk overlay (mobile needs ~6 GiB for node_modules + Metro caches)
|
|
33
|
+
* - runtime image variant (mobile uses the bun-cache pre-warmed image)
|
|
34
|
+
* - dev-server selection (mobile → Metro, web → Vite)
|
|
35
|
+
*/
|
|
36
|
+
type StackTarget = 'mobile' | 'web' | 'data' | 'native' | 'none';
|
|
37
|
+
interface StackRegistryEntry {
|
|
38
|
+
id: string;
|
|
39
|
+
target: StackTarget;
|
|
40
|
+
/**
|
|
41
|
+
* Whether this stack runs Metro/Expo bundler. Distinct from `target` —
|
|
42
|
+
* a future `flutter` stack would be `target: 'mobile'` but `metro: false`.
|
|
43
|
+
*/
|
|
44
|
+
metro?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the agent-runtime's `seedTechStack(id)` is responsible for laying
|
|
47
|
+
* down this stack's initial files. When `true`, apps/api skips copying the
|
|
48
|
+
* bundled Vite template into the workspace and instead just creates an
|
|
49
|
+
* empty directory; the agent-runtime fills it on first start. When `false`
|
|
50
|
+
* (or omitted), the legacy bundled-template path is used.
|
|
51
|
+
*
|
|
52
|
+
* The default `react-app`/`threejs-game`/`phaser-game` stacks use the
|
|
53
|
+
* bundled Vite template; everything else seeds itself.
|
|
54
|
+
*/
|
|
55
|
+
seedsOwnTemplate?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Canonical registry of every first-party tech stack we ship. Kept here in
|
|
59
|
+
* code (not loaded from disk) because `apps/api` doesn't have the
|
|
60
|
+
* `packages/agent-runtime/tech-stacks/` directory bundled into its
|
|
61
|
+
* container image, so it can't `readFileSync` stack.json at runtime.
|
|
62
|
+
*
|
|
63
|
+
* The agent-runtime side validates these entries against the on-disk
|
|
64
|
+
* stack.json files at boot — see `assertRegistryMatchesDisk()` in
|
|
65
|
+
* `packages/agent-runtime/src/workspace-defaults.ts`.
|
|
66
|
+
*/
|
|
67
|
+
declare const TECH_STACK_REGISTRY: Record<string, StackRegistryEntry>;
|
|
68
|
+
/** Lookup an entry, returning `null` for unknown ids. */
|
|
69
|
+
declare function getStackEntry(techStackId: string | null | undefined): StackRegistryEntry | null;
|
|
70
|
+
/**
|
|
71
|
+
* True for any stack whose `target` is `mobile`. Replaces the old
|
|
72
|
+
* `techStackId.startsWith('expo') || techStackId === 'react-native'`
|
|
73
|
+
* heuristic.
|
|
74
|
+
*/
|
|
75
|
+
declare function isMobileTechStack(techStackId: string | null | undefined): boolean;
|
|
76
|
+
/** True if the stack is bundled with Metro (Expo / RN). */
|
|
77
|
+
declare function usesMetroBundler(techStackId: string | null | undefined): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* True if the agent-runtime's `seedTechStack(id)` is responsible for laying
|
|
80
|
+
* down this stack's initial files. apps/api uses this to decide whether to
|
|
81
|
+
* skip copying the bundled Vite template into the workspace.
|
|
82
|
+
*
|
|
83
|
+
* Unknown stacks default to `false` (legacy bundled-template path) so that
|
|
84
|
+
* adding a new stack doesn't accidentally break the default behaviour.
|
|
85
|
+
*/
|
|
86
|
+
declare function stackSeedsItself(techStackId: string | null | undefined): boolean;
|
|
87
|
+
|
|
88
|
+
export { type StackRegistryEntry, type StackTarget, TECH_STACK_REGISTRY, getStackEntry, isMobileTechStack, stackSeedsItself, usesMetroBundler };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tech-Stack Registry
|
|
3
|
+
* --------------------------------------------------------------------------
|
|
4
|
+
* Single source of truth for "what kind of project is this?" answered at
|
|
5
|
+
* runtime by the API and the agent-runtime.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists: we used to do `techStackId.startsWith('expo')` to decide
|
|
8
|
+
* whether a project was mobile. That heuristic broke the moment we added a
|
|
9
|
+
* stack like `expo-cli-tools` (would falsely match) and would silently miss
|
|
10
|
+
* a future stack like `react-native-bare` or `flutter` (would falsely not
|
|
11
|
+
* match).
|
|
12
|
+
*
|
|
13
|
+
* The replacement is an explicit, typed map keyed by stack id, with a
|
|
14
|
+
* `target` field that captures the platform the stack actually runs on.
|
|
15
|
+
* `apps/api` reads it to size pods correctly; the agent-runtime mirrors
|
|
16
|
+
* the same metadata in each stack's `stack.json` so the registry and the
|
|
17
|
+
* stack files stay in sync.
|
|
18
|
+
*
|
|
19
|
+
* Adding a new stack:
|
|
20
|
+
* 1. Add an entry below with `target: 'mobile' | 'web' | 'data' | 'native'`.
|
|
21
|
+
* 2. Add the matching `target` field to the stack's `stack.json`.
|
|
22
|
+
* 3. The runtime/API automatically pick up the new stack's class —
|
|
23
|
+
* no `if (id.startsWith(…))` to update.
|
|
24
|
+
*
|
|
25
|
+
* Lifted into the SDK from `@shogo/shared-runtime` (was AGPL) under MIT.
|
|
26
|
+
* The original `@shogo/shared-runtime/tech-stack-registry` re-exports this
|
|
27
|
+
* module unchanged for backwards compatibility.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Platform a stack ultimately runs on. Drives:
|
|
31
|
+
* - instance-size floors (mobile needs `small`+ for Metro/RN node_modules)
|
|
32
|
+
* - disk overlay (mobile needs ~6 GiB for node_modules + Metro caches)
|
|
33
|
+
* - runtime image variant (mobile uses the bun-cache pre-warmed image)
|
|
34
|
+
* - dev-server selection (mobile → Metro, web → Vite)
|
|
35
|
+
*/
|
|
36
|
+
type StackTarget = 'mobile' | 'web' | 'data' | 'native' | 'none';
|
|
37
|
+
interface StackRegistryEntry {
|
|
38
|
+
id: string;
|
|
39
|
+
target: StackTarget;
|
|
40
|
+
/**
|
|
41
|
+
* Whether this stack runs Metro/Expo bundler. Distinct from `target` —
|
|
42
|
+
* a future `flutter` stack would be `target: 'mobile'` but `metro: false`.
|
|
43
|
+
*/
|
|
44
|
+
metro?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the agent-runtime's `seedTechStack(id)` is responsible for laying
|
|
47
|
+
* down this stack's initial files. When `true`, apps/api skips copying the
|
|
48
|
+
* bundled Vite template into the workspace and instead just creates an
|
|
49
|
+
* empty directory; the agent-runtime fills it on first start. When `false`
|
|
50
|
+
* (or omitted), the legacy bundled-template path is used.
|
|
51
|
+
*
|
|
52
|
+
* The default `react-app`/`threejs-game`/`phaser-game` stacks use the
|
|
53
|
+
* bundled Vite template; everything else seeds itself.
|
|
54
|
+
*/
|
|
55
|
+
seedsOwnTemplate?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Canonical registry of every first-party tech stack we ship. Kept here in
|
|
59
|
+
* code (not loaded from disk) because `apps/api` doesn't have the
|
|
60
|
+
* `packages/agent-runtime/tech-stacks/` directory bundled into its
|
|
61
|
+
* container image, so it can't `readFileSync` stack.json at runtime.
|
|
62
|
+
*
|
|
63
|
+
* The agent-runtime side validates these entries against the on-disk
|
|
64
|
+
* stack.json files at boot — see `assertRegistryMatchesDisk()` in
|
|
65
|
+
* `packages/agent-runtime/src/workspace-defaults.ts`.
|
|
66
|
+
*/
|
|
67
|
+
declare const TECH_STACK_REGISTRY: Record<string, StackRegistryEntry>;
|
|
68
|
+
/** Lookup an entry, returning `null` for unknown ids. */
|
|
69
|
+
declare function getStackEntry(techStackId: string | null | undefined): StackRegistryEntry | null;
|
|
70
|
+
/**
|
|
71
|
+
* True for any stack whose `target` is `mobile`. Replaces the old
|
|
72
|
+
* `techStackId.startsWith('expo') || techStackId === 'react-native'`
|
|
73
|
+
* heuristic.
|
|
74
|
+
*/
|
|
75
|
+
declare function isMobileTechStack(techStackId: string | null | undefined): boolean;
|
|
76
|
+
/** True if the stack is bundled with Metro (Expo / RN). */
|
|
77
|
+
declare function usesMetroBundler(techStackId: string | null | undefined): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* True if the agent-runtime's `seedTechStack(id)` is responsible for laying
|
|
80
|
+
* down this stack's initial files. apps/api uses this to decide whether to
|
|
81
|
+
* skip copying the bundled Vite template into the workspace.
|
|
82
|
+
*
|
|
83
|
+
* Unknown stacks default to `false` (legacy bundled-template path) so that
|
|
84
|
+
* adding a new stack doesn't accidentally break the default behaviour.
|
|
85
|
+
*/
|
|
86
|
+
declare function stackSeedsItself(techStackId: string | null | undefined): boolean;
|
|
87
|
+
|
|
88
|
+
export { type StackRegistryEntry, type StackTarget, TECH_STACK_REGISTRY, getStackEntry, isMobileTechStack, stackSeedsItself, usesMetroBundler };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TECH_STACK_REGISTRY,
|
|
3
|
+
getStackEntry,
|
|
4
|
+
isMobileTechStack,
|
|
5
|
+
stackSeedsItself,
|
|
6
|
+
usesMetroBundler
|
|
7
|
+
} from "./chunk-KTDDFWXM.js";
|
|
8
|
+
export {
|
|
9
|
+
TECH_STACK_REGISTRY,
|
|
10
|
+
getStackEntry,
|
|
11
|
+
isMobileTechStack,
|
|
12
|
+
stackSeedsItself,
|
|
13
|
+
usesMetroBundler
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=tech-stack-registry.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shogo-ai/core",
|
|
3
|
+
"version": "1.4.1",
|
|
4
|
+
"description": "Shogo Core — generic utilities (logger, instrumentation, stream-buffer, chat-message). Zero required peers.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.cjs",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"sideEffects": false,
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./logger": {
|
|
18
|
+
"types": "./dist/logger.d.ts",
|
|
19
|
+
"import": "./dist/logger.js",
|
|
20
|
+
"require": "./dist/logger.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./instrumentation": {
|
|
23
|
+
"types": "./dist/instrumentation.d.ts",
|
|
24
|
+
"import": "./dist/instrumentation.js",
|
|
25
|
+
"require": "./dist/instrumentation.cjs"
|
|
26
|
+
},
|
|
27
|
+
"./stream-buffer": {
|
|
28
|
+
"types": "./dist/stream-buffer.d.ts",
|
|
29
|
+
"import": "./dist/stream-buffer.js",
|
|
30
|
+
"require": "./dist/stream-buffer.cjs"
|
|
31
|
+
},
|
|
32
|
+
"./chat-message": {
|
|
33
|
+
"types": "./dist/chat-message.d.ts",
|
|
34
|
+
"import": "./dist/chat-message.js",
|
|
35
|
+
"require": "./dist/chat-message.cjs"
|
|
36
|
+
},
|
|
37
|
+
"./macos-junk": {
|
|
38
|
+
"types": "./dist/macos-junk.d.ts",
|
|
39
|
+
"import": "./dist/macos-junk.js",
|
|
40
|
+
"require": "./dist/macos-junk.cjs"
|
|
41
|
+
},
|
|
42
|
+
"./tech-stack-registry": {
|
|
43
|
+
"types": "./dist/tech-stack-registry.d.ts",
|
|
44
|
+
"import": "./dist/tech-stack-registry.js",
|
|
45
|
+
"require": "./dist/tech-stack-registry.cjs"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist/**/*.js",
|
|
50
|
+
"dist/**/*.cjs",
|
|
51
|
+
"dist/**/*.d.ts",
|
|
52
|
+
"dist/**/*.d.cts"
|
|
53
|
+
],
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"dev": "tsup --watch",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"test": "bun --no-env-file test src/"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"@opentelemetry/api": ">=1.9.0",
|
|
62
|
+
"@opentelemetry/sdk-node": ">=0.213.0",
|
|
63
|
+
"@opentelemetry/exporter-trace-otlp-http": ">=0.213.0",
|
|
64
|
+
"@opentelemetry/sdk-trace-base": ">=2.5.0",
|
|
65
|
+
"@opentelemetry/resources": ">=2.5.0",
|
|
66
|
+
"@opentelemetry/semantic-conventions": ">=1.39.0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"@opentelemetry/api": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
72
|
+
"@opentelemetry/sdk-node": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@opentelemetry/exporter-trace-otlp-http": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"@opentelemetry/sdk-trace-base": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"@opentelemetry/resources": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"@opentelemetry/semantic-conventions": {
|
|
85
|
+
"optional": true
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"@opentelemetry/api": "^1.9.0",
|
|
90
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.213.0",
|
|
91
|
+
"@opentelemetry/resources": "^2.5.1",
|
|
92
|
+
"@opentelemetry/sdk-node": "^0.213.0",
|
|
93
|
+
"@opentelemetry/sdk-trace-base": "^2.5.1",
|
|
94
|
+
"@opentelemetry/semantic-conventions": "^1.39.0",
|
|
95
|
+
"@types/node": "^25.5.0",
|
|
96
|
+
"tsup": "^8.0.0",
|
|
97
|
+
"typescript": "^6.0.2"
|
|
98
|
+
},
|
|
99
|
+
"publishConfig": {
|
|
100
|
+
"access": "public"
|
|
101
|
+
},
|
|
102
|
+
"keywords": [
|
|
103
|
+
"shogo",
|
|
104
|
+
"logger",
|
|
105
|
+
"opentelemetry",
|
|
106
|
+
"stream-buffer"
|
|
107
|
+
],
|
|
108
|
+
"author": "Shogo Labs",
|
|
109
|
+
"repository": {
|
|
110
|
+
"type": "git",
|
|
111
|
+
"url": "git+https://github.com/shogo-labs/shogo-ai.git",
|
|
112
|
+
"directory": "packages/core"
|
|
113
|
+
},
|
|
114
|
+
"homepage": "https://github.com/shogo-labs/shogo-ai/tree/main/packages/core#readme",
|
|
115
|
+
"bugs": {
|
|
116
|
+
"url": "https://github.com/shogo-labs/shogo-ai/issues"
|
|
117
|
+
}
|
|
118
|
+
}
|